mathexpr.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /****************************************************************************** | ||
| 2 | ** MODULE: MATHEXPR.C | ||
| 3 | ** PROJECT: EPA SWMM5 | ||
| 4 | ** DESCRIPTION: Evaluates symbolic mathematical expression consisting | ||
| 5 | ** of numbers, variable names, math functions & arithmetic | ||
| 6 | ** operators. | ||
| 7 | ** AUTHORS: L. Rossman, US EPA - NRMRL | ||
| 8 | ** F. Shang, University of Cincinnati | ||
| 9 | ** VERSION: 5.2.2 | ||
| 10 | ** LAST UPDATE: 09/02/2022 | ||
| 11 | ** BUG FIXES: Problems related to '^' operator (F. Shang, 09/02/2022) | ||
| 12 | ******************************************************************************/ | ||
| 13 | /* | ||
| 14 | ** Operand codes: | ||
| 15 | ** 1 = ( | ||
| 16 | ** 2 = ) | ||
| 17 | ** 3 = + | ||
| 18 | ** 4 = - (subtraction) | ||
| 19 | ** 5 = * | ||
| 20 | ** 6 = / | ||
| 21 | ** 7 = number | ||
| 22 | ** 8 = user-defined variable | ||
| 23 | ** 9 = - (negative) | ||
| 24 | ** 10 = cos | ||
| 25 | ** 11 = sin | ||
| 26 | ** 12 = tan | ||
| 27 | ** 13 = cot | ||
| 28 | ** 14 = abs | ||
| 29 | ** 15 = sgn | ||
| 30 | ** 16 = sqrt | ||
| 31 | ** 17 = log | ||
| 32 | ** 18 = exp | ||
| 33 | ** 19 = asin | ||
| 34 | ** 20 = acos | ||
| 35 | ** 21 = atan | ||
| 36 | ** 22 = acot | ||
| 37 | ** 23 = sinh | ||
| 38 | ** 24 = cosh | ||
| 39 | ** 25 = tanh | ||
| 40 | ** 26 = coth | ||
| 41 | ** 27 = log10 | ||
| 42 | ** 28 = step (x<=0 ? 0 : 1) | ||
| 43 | ** 31 = ^ | ||
| 44 | ******************************************************************************/ | ||
| 45 | #define _CRT_SECURE_NO_DEPRECATE | ||
| 46 | |||
| 47 | #include <ctype.h> | ||
| 48 | #include <stdlib.h> | ||
| 49 | #include <string.h> | ||
| 50 | #include <stdlib.h> | ||
| 51 | #include <math.h> | ||
| 52 | #include "mathexpr.h" | ||
| 53 | |||
| 54 | #define MAX_STACK_SIZE 1024 | ||
| 55 | |||
| 56 | // Local declarations | ||
| 57 | //-------------------- | ||
| 58 | // Structure for binary tree representation of math expression | ||
| 59 | struct TreeNode | ||
| 60 | { | ||
| 61 | int opcode; // operator code | ||
| 62 | int ivar; // variable index | ||
| 63 | double fvalue; // numerical value | ||
| 64 | struct TreeNode *left; // left sub-tree of tokenized formula | ||
| 65 | struct TreeNode *right; // right sub-tree of tokenized formula | ||
| 66 | }; | ||
| 67 | typedef struct TreeNode ExprTree; | ||
| 68 | |||
| 69 | // Local variables | ||
| 70 | //---------------- | ||
| 71 | static int Err; | ||
| 72 | static int Bc; | ||
| 73 | static int PrevLex, CurLex; | ||
| 74 | static int Len, Pos; | ||
| 75 | static char *S; | ||
| 76 | static char Token[255]; | ||
| 77 | static int Ivar; | ||
| 78 | static double Fvalue; | ||
| 79 | |||
| 80 | // math function names | ||
| 81 | char *MathFunc[] = {"COS", "SIN", "TAN", "COT", "ABS", "SGN", | ||
| 82 | "SQRT", "LOG", "EXP", "ASIN", "ACOS", "ATAN", | ||
| 83 | "ACOT", "SINH", "COSH", "TANH", "COTH", "LOG10", | ||
| 84 | "STEP", NULL}; | ||
| 85 | |||
| 86 | // Local functions | ||
| 87 | //---------------- | ||
| 88 | static int sametext(char *, char *); | ||
| 89 | static int isDigit(char); | ||
| 90 | static int isLetter(char); | ||
| 91 | static void getToken(void); | ||
| 92 | static int getMathFunc(void); | ||
| 93 | static int getVariable(void); | ||
| 94 | static int getOperand(void); | ||
| 95 | static int getLex(void); | ||
| 96 | static double getNumber(void); | ||
| 97 | static ExprTree * newNode(void); | ||
| 98 | static ExprTree * getSingleOp(int *); | ||
| 99 | static ExprTree * getOp(int *); | ||
| 100 | static ExprTree * getTree(void); | ||
| 101 | static void traverseTree(ExprTree *, MathExpr **); | ||
| 102 | static void deleteTree(ExprTree *); | ||
| 103 | |||
| 104 | // Callback functions | ||
| 105 | static int (*getVariableIndex) (char *); // return index of named variable | ||
| 106 | |||
| 107 | //============================================================================= | ||
| 108 | |||
| 109 | 1558 | int sametext(char *s1, char *s2) | |
| 110 | /* | ||
| 111 | ** Purpose: | ||
| 112 | ** performs case insensitive comparison of two strings. | ||
| 113 | ** | ||
| 114 | ** Input: | ||
| 115 | ** s1 = character string | ||
| 116 | ** s2 = character string. | ||
| 117 | ** | ||
| 118 | ** Returns: | ||
| 119 | ** 1 if strings are the same, 0 otherwise. | ||
| 120 | */ | ||
| 121 | { | ||
| 122 | int i; | ||
| 123 |
2/2✓ Branch 0 taken 168 times.
✓ Branch 1 taken 1539 times.
|
1707 | for (i=0; toupper(s1[i]) == toupper(s2[i]); i++) |
| 124 |
4/4✓ Branch 0 taken 24 times.
✓ Branch 1 taken 144 times.
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 5 times.
|
168 | if (!s1[i+1] && !s2[i+1]) return(1); |
| 125 | 1539 | return(0); | |
| 126 | } | ||
| 127 | |||
| 128 | //============================================================================= | ||
| 129 | |||
| 130 | 435 | int isDigit(char c) | |
| 131 | { | ||
| 132 |
4/4✓ Branch 0 taken 98 times.
✓ Branch 1 taken 337 times.
✓ Branch 2 taken 94 times.
✓ Branch 3 taken 4 times.
|
435 | if (c >= '1' && c <= '9') return 1; |
| 133 |
2/2✓ Branch 0 taken 130 times.
✓ Branch 1 taken 211 times.
|
341 | if (c == '0') return 1; |
| 134 | 211 | return 0; | |
| 135 | } | ||
| 136 | |||
| 137 | //============================================================================= | ||
| 138 | |||
| 139 | 789 | int isLetter(char c) | |
| 140 | { | ||
| 141 |
3/4✓ Branch 0 taken 56 times.
✓ Branch 1 taken 733 times.
✓ Branch 2 taken 56 times.
✗ Branch 3 not taken.
|
789 | if (c >= 'a' && c <= 'z') return 1; |
| 142 |
4/4✓ Branch 0 taken 579 times.
✓ Branch 1 taken 154 times.
✓ Branch 2 taken 547 times.
✓ Branch 3 taken 32 times.
|
733 | if (c >= 'A' && c <= 'Z') return 1; |
| 143 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 156 times.
|
186 | if (c == '_') return 1; |
| 144 | 156 | return 0; | |
| 145 | } | ||
| 146 | |||
| 147 | //============================================================================= | ||
| 148 | |||
| 149 | 91 | void getToken() | |
| 150 | { | ||
| 151 | 91 | char c[] = " "; | |
| 152 | 91 | Token[0] = '\0'; | |
| 153 |
3/4✓ Branch 0 taken 637 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 542 times.
✓ Branch 3 taken 95 times.
|
1274 | while ( Pos <= Len && |
| 154 |
2/2✓ Branch 2 taken 4 times.
✓ Branch 3 taken 91 times.
|
732 | ( isLetter(S[Pos]) || isDigit(S[Pos]) ) ) |
| 155 | { | ||
| 156 | 546 | c[0] = S[Pos]; | |
| 157 | 546 | strcat(Token, c); | |
| 158 | 546 | Pos++; | |
| 159 | } | ||
| 160 | 91 | Pos--; | |
| 161 | 91 | } | |
| 162 | |||
| 163 | //============================================================================= | ||
| 164 | |||
| 165 | 91 | int getMathFunc() | |
| 166 | { | ||
| 167 | 91 | int i = 0; | |
| 168 |
2/2✓ Branch 0 taken 1558 times.
✓ Branch 1 taken 72 times.
|
1630 | while (MathFunc[i] != NULL) |
| 169 | { | ||
| 170 |
2/2✓ Branch 1 taken 19 times.
✓ Branch 2 taken 1539 times.
|
1558 | if (sametext(MathFunc[i], Token)) return i+10; |
| 171 | 1539 | i++; | |
| 172 | } | ||
| 173 | 72 | return(0); | |
| 174 | } | ||
| 175 | |||
| 176 | //============================================================================= | ||
| 177 | |||
| 178 | 72 | int getVariable() | |
| 179 | { | ||
| 180 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
|
72 | if ( !getVariableIndex ) return 0; |
| 181 | 72 | Ivar = getVariableIndex(Token); | |
| 182 |
1/2✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
|
72 | if (Ivar >= 0) return 8; |
| 183 | ✗ | return 0; | |
| 184 | } | ||
| 185 | |||
| 186 | //============================================================================= | ||
| 187 | |||
| 188 | 61 | double getNumber() | |
| 189 | { | ||
| 190 | 61 | char c[] = " "; | |
| 191 | char sNumber[255]; | ||
| 192 | 61 | int errflag = 0; | |
| 193 | 61 | int decimalCount = 0; | |
| 194 | |||
| 195 | /* --- get whole number portion of number */ | ||
| 196 | 61 | sNumber[0] = '\0'; | |
| 197 |
4/4✓ Branch 0 taken 122 times.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 62 times.
✓ Branch 4 taken 60 times.
|
123 | while (Pos < Len && isDigit(S[Pos])) |
| 198 | { | ||
| 199 | 62 | c[0] = S[Pos]; | |
| 200 | 62 | strcat(sNumber, c); | |
| 201 | 62 | Pos++; | |
| 202 | } | ||
| 203 | |||
| 204 | /* --- get fractional portion of number */ | ||
| 205 |
2/2✓ Branch 0 taken 60 times.
✓ Branch 1 taken 1 time.
|
61 | if (Pos < Len) |
| 206 | { | ||
| 207 |
1/2✓ Branch 0 taken 60 times.
✗ Branch 1 not taken.
|
60 | if (S[Pos] == '.') |
| 208 | { | ||
| 209 | 60 | decimalCount++; | |
| 210 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
|
60 | if (decimalCount > 1) Err = 1; |
| 211 | 60 | strcat(sNumber, "."); | |
| 212 | 60 | Pos++; | |
| 213 |
4/4✓ Branch 0 taken 152 times.
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 97 times.
✓ Branch 4 taken 55 times.
|
157 | while (Pos < Len && isDigit(S[Pos])) |
| 214 | { | ||
| 215 | 97 | c[0] = S[Pos]; | |
| 216 | 97 | strcat(sNumber, c); | |
| 217 | 97 | Pos++; | |
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | /* --- get exponent */ | ||
| 222 |
4/6✓ Branch 0 taken 55 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 55 times.
|
60 | if (Pos < Len && (S[Pos] == 'e' || S[Pos] == 'E')) |
| 223 | { | ||
| 224 | ✗ | strcat(sNumber, "E"); | |
| 225 | ✗ | Pos++; | |
| 226 | ✗ | if (Pos >= Len) errflag = 1; | |
| 227 | else | ||
| 228 | { | ||
| 229 | ✗ | if (S[Pos] == '-' || S[Pos] == '+') | |
| 230 | { | ||
| 231 | ✗ | c[0] = S[Pos]; | |
| 232 | ✗ | strcat(sNumber, c); | |
| 233 | ✗ | Pos++; | |
| 234 | } | ||
| 235 | ✗ | if (Pos >= Len || !isDigit(S[Pos])) errflag = 1; | |
| 236 | ✗ | else while ( Pos < Len && isDigit(S[Pos])) | |
| 237 | { | ||
| 238 | ✗ | c[0] = S[Pos]; | |
| 239 | ✗ | strcat(sNumber, c); | |
| 240 | ✗ | Pos++; | |
| 241 | } | ||
| 242 | } | ||
| 243 | } | ||
| 244 | } | ||
| 245 | 61 | Pos--; | |
| 246 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
|
61 | if (errflag) return 0; |
| 247 | 61 | else return atof(sNumber); | |
| 248 | } | ||
| 249 | |||
| 250 | //============================================================================= | ||
| 251 | |||
| 252 | 306 | int getOperand() | |
| 253 | { | ||
| 254 | int code; | ||
| 255 |
8/8✓ Branch 0 taken 24 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 45 times.
✓ Branch 5 taken 21 times.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 152 times.
|
306 | switch(S[Pos]) |
| 256 | { | ||
| 257 | 24 | case '(': code = 1; break; | |
| 258 | 24 | case ')': code = 2; break; | |
| 259 | 32 | case '+': code = 3; break; | |
| 260 | 5 | case '-': code = 4; | |
| 261 |
2/4✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
|
10 | if (Pos < Len-1 && |
| 262 | 5 | isDigit(S[Pos+1]) && | |
| 263 | ✗ | (CurLex <= 6 || CurLex == 31)) | |
| 264 | { | ||
| 265 | ✗ | Pos++; | |
| 266 | ✗ | Fvalue = -getNumber(); | |
| 267 | ✗ | code = 7; | |
| 268 | } | ||
| 269 | 5 | break; | |
| 270 | 45 | case '*': code = 5; break; | |
| 271 | 21 | case '/': code = 6; break; | |
| 272 | 3 | case '^': code = 31; break; | |
| 273 | 152 | default: code = 0; | |
| 274 | } | ||
| 275 | 306 | return code; | |
| 276 | } | ||
| 277 | |||
| 278 | //============================================================================= | ||
| 279 | |||
| 280 | 335 | int getLex() | |
| 281 | { | ||
| 282 | int n; | ||
| 283 | |||
| 284 | /* --- skip spaces */ | ||
| 285 |
4/4✓ Branch 0 taken 462 times.
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 156 times.
✓ Branch 3 taken 306 times.
|
491 | while ( Pos < Len && S[Pos] == ' ' ) Pos++; |
| 286 |
2/2✓ Branch 0 taken 29 times.
✓ Branch 1 taken 306 times.
|
335 | if ( Pos >= Len ) return 0; |
| 287 | |||
| 288 | /* --- check for operand */ | ||
| 289 | 306 | n = getOperand(); | |
| 290 | |||
| 291 | /* --- check for function/variable/number */ | ||
| 292 |
2/2✓ Branch 0 taken 152 times.
✓ Branch 1 taken 154 times.
|
306 | if ( n == 0 ) |
| 293 | { | ||
| 294 |
2/2✓ Branch 1 taken 91 times.
✓ Branch 2 taken 61 times.
|
152 | if ( isLetter(S[Pos]) ) |
| 295 | { | ||
| 296 | 91 | getToken(); | |
| 297 | 91 | n = getMathFunc(); | |
| 298 |
2/2✓ Branch 0 taken 72 times.
✓ Branch 1 taken 19 times.
|
91 | if ( n == 0 ) n = getVariable(); |
| 299 | } | ||
| 300 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 61 times.
✗ Branch 4 not taken.
|
61 | else if ( S[Pos] == '.' || isDigit(S[Pos]) ) |
| 301 | { | ||
| 302 | 61 | n = 7; | |
| 303 | 61 | Fvalue = getNumber(); | |
| 304 | } | ||
| 305 | } | ||
| 306 | 306 | Pos++; | |
| 307 | 306 | PrevLex = CurLex; | |
| 308 | 306 | CurLex = n; | |
| 309 | 306 | return n; | |
| 310 | } | ||
| 311 | |||
| 312 | //============================================================================= | ||
| 313 | |||
| 314 | 258 | ExprTree * newNode() | |
| 315 | { | ||
| 316 | ExprTree *node; | ||
| 317 | 258 | node = (ExprTree *) malloc(sizeof(ExprTree)); | |
| 318 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 258 times.
|
258 | if (!node) Err = 2; |
| 319 | else | ||
| 320 | { | ||
| 321 | 258 | node->opcode = 0; | |
| 322 | 258 | node->ivar = -1; | |
| 323 | 258 | node->fvalue = 0.; | |
| 324 | 258 | node->left = NULL; | |
| 325 | 258 | node->right = NULL; | |
| 326 | } | ||
| 327 | 258 | return node; | |
| 328 | } | ||
| 329 | |||
| 330 | //============================================================================= | ||
| 331 | |||
| 332 | 157 | ExprTree * getSingleOp(int *lex) | |
| 333 | { | ||
| 334 | int opcode; | ||
| 335 | ExprTree *left; | ||
| 336 | ExprTree *node; | ||
| 337 | |||
| 338 | /* --- open parenthesis, so continue to grow the tree */ | ||
| 339 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 152 times.
|
157 | if ( *lex == 1 ) |
| 340 | { | ||
| 341 | 5 | Bc++; | |
| 342 | 5 | left = getTree(); | |
| 343 | } | ||
| 344 | |||
| 345 | else | ||
| 346 | { | ||
| 347 | /* --- Error if not a singleton operand */ | ||
| 348 |
3/6✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 152 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 152 times.
|
152 | if ( *lex < 7 || *lex == 9 || *lex > 30) |
| 349 | { | ||
| 350 | ✗ | Err = 1; | |
| 351 | ✗ | return NULL; | |
| 352 | } | ||
| 353 | |||
| 354 | 152 | opcode = *lex; | |
| 355 | |||
| 356 | /* --- simple number or variable name */ | ||
| 357 |
4/4✓ Branch 0 taken 91 times.
✓ Branch 1 taken 61 times.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 19 times.
|
152 | if ( *lex == 7 || *lex == 8 ) |
| 358 | { | ||
| 359 | 133 | left = newNode(); | |
| 360 | 133 | left->opcode = opcode; | |
| 361 |
2/2✓ Branch 0 taken 61 times.
✓ Branch 1 taken 72 times.
|
133 | if ( *lex == 7 ) left->fvalue = Fvalue; |
| 362 |
2/2✓ Branch 0 taken 72 times.
✓ Branch 1 taken 61 times.
|
133 | if ( *lex == 8 ) left->ivar = Ivar; |
| 363 | } | ||
| 364 | |||
| 365 | /* --- function which must have a '(' after it */ | ||
| 366 | else | ||
| 367 | { | ||
| 368 | 19 | *lex = getLex(); | |
| 369 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
|
19 | if ( *lex != 1 ) |
| 370 | { | ||
| 371 | ✗ | Err = 1; | |
| 372 | ✗ | return NULL; | |
| 373 | } | ||
| 374 | 19 | Bc++; | |
| 375 | 19 | left = newNode(); | |
| 376 | 19 | left->left = getTree(); | |
| 377 | 19 | left->opcode = opcode; | |
| 378 | } | ||
| 379 | } | ||
| 380 | 157 | *lex = getLex(); | |
| 381 | |||
| 382 | /* --- exponentiation */ | ||
| 383 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 154 times.
|
157 | if (*lex == 31) |
| 384 | { | ||
| 385 | 3 | node = newNode(); | |
| 386 | 3 | node->left = left; | |
| 387 | 3 | node->opcode = *lex; | |
| 388 | 3 | *lex = getLex(); | |
| 389 | 3 | node->right = getSingleOp(lex); | |
| 390 | 3 | left = node; | |
| 391 | } | ||
| 392 | 157 | return left; | |
| 393 | } | ||
| 394 | |||
| 395 | //============================================================================= | ||
| 396 | |||
| 397 | 88 | ExprTree * getOp(int *lex) | |
| 398 | { | ||
| 399 | int opcode; | ||
| 400 | ExprTree *left; | ||
| 401 | ExprTree *right; | ||
| 402 | ExprTree *node; | ||
| 403 | 88 | int neg = 0; | |
| 404 | |||
| 405 | 88 | *lex = getLex(); | |
| 406 |
4/4✓ Branch 0 taken 59 times.
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 35 times.
|
88 | if (PrevLex == 0 || PrevLex == 1) |
| 407 | { | ||
| 408 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 51 times.
|
53 | if ( *lex == 4 ) |
| 409 | { | ||
| 410 | 2 | neg = 1; | |
| 411 | 2 | *lex = getLex(); | |
| 412 | } | ||
| 413 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
|
51 | else if ( *lex == 3) *lex = getLex(); |
| 414 | } | ||
| 415 | 88 | left = getSingleOp(lex); | |
| 416 |
4/4✓ Branch 0 taken 45 times.
✓ Branch 1 taken 109 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 88 times.
|
154 | while ( *lex == 5 || *lex == 6) |
| 417 | { | ||
| 418 | 66 | opcode = *lex; | |
| 419 | 66 | *lex = getLex(); | |
| 420 | 66 | right = getSingleOp(lex); | |
| 421 | 66 | node = newNode(); | |
| 422 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
|
66 | if (Err) return NULL; |
| 423 | 66 | node->left = left; | |
| 424 | 66 | node->right = right; | |
| 425 | 66 | node->opcode = opcode; | |
| 426 | 66 | left = node; | |
| 427 | } | ||
| 428 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 86 times.
|
88 | if ( neg ) |
| 429 | { | ||
| 430 | 2 | node = newNode(); | |
| 431 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | if (Err) return NULL; |
| 432 | 2 | node->left = left; | |
| 433 | 2 | node->right = NULL; | |
| 434 | 2 | node->opcode = 9; | |
| 435 | 2 | left = node; | |
| 436 | } | ||
| 437 | 88 | return left; | |
| 438 | } | ||
| 439 | |||
| 440 | //============================================================================= | ||
| 441 | |||
| 442 | 53 | ExprTree * getTree() | |
| 443 | { | ||
| 444 | int lex; | ||
| 445 | int opcode; | ||
| 446 | ExprTree *left; | ||
| 447 | ExprTree *right; | ||
| 448 | ExprTree *node; | ||
| 449 | |||
| 450 | 53 | left = getOp(&lex); | |
| 451 | for (;;) | ||
| 452 | { | ||
| 453 |
4/4✓ Branch 0 taken 59 times.
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 35 times.
|
88 | if ( lex == 0 || lex == 2 ) |
| 454 | { | ||
| 455 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 29 times.
|
53 | if ( lex == 2 ) Bc--; |
| 456 | 53 | break; | |
| 457 | } | ||
| 458 | |||
| 459 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
35 | if (lex != 3 && lex != 4 ) |
| 460 | { | ||
| 461 | ✗ | Err = 1; | |
| 462 | ✗ | break; | |
| 463 | } | ||
| 464 | |||
| 465 | 35 | opcode = lex; | |
| 466 | 35 | right = getOp(&lex); | |
| 467 | 35 | node = newNode(); | |
| 468 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
|
35 | if (Err) break; |
| 469 | 35 | node->left = left; | |
| 470 | 35 | node->right = right; | |
| 471 | 35 | node->opcode = opcode; | |
| 472 | 35 | left = node; | |
| 473 | } | ||
| 474 | 53 | return left; | |
| 475 | } | ||
| 476 | |||
| 477 | //============================================================================= | ||
| 478 | |||
| 479 | 545 | void traverseTree(ExprTree *tree, MathExpr **expr) | |
| 480 | // Converts binary tree to linked list (postfix format) | ||
| 481 | { | ||
| 482 | MathExpr *node; | ||
| 483 |
2/2✓ Branch 0 taken 287 times.
✓ Branch 1 taken 258 times.
|
545 | if ( tree == NULL) return; |
| 484 | 258 | traverseTree(tree->left, expr); | |
| 485 | 258 | traverseTree(tree->right, expr); | |
| 486 | 258 | node = (MathExpr *) malloc(sizeof(MathExpr)); | |
| 487 |
1/2✓ Branch 0 taken 258 times.
✗ Branch 1 not taken.
|
258 | if (node) |
| 488 | { | ||
| 489 | 258 | node->fvalue = tree->fvalue; | |
| 490 | 258 | node->opcode = tree->opcode; | |
| 491 | 258 | node->ivar = tree->ivar; | |
| 492 | 258 | node->next = NULL; | |
| 493 | 258 | node->prev = (*expr); | |
| 494 | } | ||
| 495 |
2/2✓ Branch 0 taken 229 times.
✓ Branch 1 taken 29 times.
|
258 | if (*expr) (*expr)->next = node; |
| 496 | 258 | (*expr) = node; | |
| 497 | } | ||
| 498 | |||
| 499 | //============================================================================= | ||
| 500 | |||
| 501 | 258 | void deleteTree(ExprTree *tree) | |
| 502 | { | ||
| 503 |
1/2✓ Branch 0 taken 258 times.
✗ Branch 1 not taken.
|
258 | if (tree) |
| 504 | { | ||
| 505 |
2/2✓ Branch 0 taken 125 times.
✓ Branch 1 taken 133 times.
|
258 | if (tree->left) deleteTree(tree->left); |
| 506 |
2/2✓ Branch 0 taken 104 times.
✓ Branch 1 taken 154 times.
|
258 | if (tree->right) deleteTree(tree->right); |
| 507 | 258 | free(tree); | |
| 508 | } | ||
| 509 | 258 | } | |
| 510 | |||
| 511 | //============================================================================= | ||
| 512 | |||
| 513 | // Turn on "precise" floating point option | ||
| 514 | #pragma float_control(precise, on, push) | ||
| 515 | |||
| 516 | 289812 | double mathexpr_eval(MathExpr *expr, double (*getVariableValue) (int)) | |
| 517 | // Mathematica expression evaluation using a stack | ||
| 518 | { | ||
| 519 | |||
| 520 | // --- Note: the ExprStack array must be declared locally and not globally | ||
| 521 | // since this function can be called recursively. | ||
| 522 | |||
| 523 | double ExprStack[MAX_STACK_SIZE]; | ||
| 524 | 289812 | MathExpr *node = expr; | |
| 525 | double r1, r2; | ||
| 526 | 289812 | int stackindex = 0; | |
| 527 | |||
| 528 | 289812 | ExprStack[0] = 0.0; | |
| 529 |
3/4✓ Branch 0 taken 997352 times.
✓ Branch 1 taken 289812 times.
✓ Branch 2 taken 997352 times.
✗ Branch 3 not taken.
|
1287164 | while(node != NULL && stackindex >= 0) |
| 530 | { | ||
| 531 |
27/28✓ Branch 0 taken 9130 times.
✓ Branch 1 taken 720 times.
✓ Branch 2 taken 175580 times.
✓ Branch 3 taken 164520 times.
✓ Branch 4 taken 140042 times.
✓ Branch 5 taken 501020 times.
✓ Branch 6 taken 480 times.
✓ Branch 7 taken 240 times.
✓ Branch 8 taken 240 times.
✓ Branch 9 taken 240 times.
✓ Branch 10 taken 240 times.
✓ Branch 11 taken 240 times.
✓ Branch 12 taken 240 times.
✓ Branch 13 taken 240 times.
✓ Branch 14 taken 240 times.
✓ Branch 15 taken 240 times.
✓ Branch 16 taken 240 times.
✓ Branch 17 taken 240 times.
✓ Branch 18 taken 240 times.
✓ Branch 19 taken 240 times.
✓ Branch 20 taken 240 times.
✓ Branch 21 taken 240 times.
✓ Branch 22 taken 240 times.
✓ Branch 23 taken 240 times.
✓ Branch 24 taken 240 times.
✓ Branch 25 taken 240 times.
✓ Branch 26 taken 1300 times.
✗ Branch 27 not taken.
|
997352 | switch (node->opcode) |
| 532 | { | ||
| 533 | 9130 | case 3: | |
| 534 | 9130 | r1 = ExprStack[stackindex]; | |
| 535 | 9130 | stackindex--; | |
| 536 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9130 times.
|
9130 | if (stackindex < 0) break; |
| 537 | 9130 | r2 = ExprStack[stackindex]; | |
| 538 | 9130 | ExprStack[stackindex] = r2 + r1; | |
| 539 | 9130 | break; | |
| 540 | |||
| 541 | 720 | case 4: | |
| 542 | 720 | r1 = ExprStack[stackindex]; | |
| 543 | 720 | stackindex--; | |
| 544 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 720 times.
|
720 | if (stackindex < 0) break; |
| 545 | 720 | r2 = ExprStack[stackindex]; | |
| 546 | 720 | ExprStack[stackindex] = r2 - r1; | |
| 547 | 720 | break; | |
| 548 | |||
| 549 | 175580 | case 5: | |
| 550 | 175580 | r1 = ExprStack[stackindex]; | |
| 551 | 175580 | stackindex--; | |
| 552 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 175580 times.
|
175580 | if (stackindex < 0) break; |
| 553 | 175580 | r2 = ExprStack[stackindex]; | |
| 554 | 175580 | ExprStack[stackindex] = r2 * r1; | |
| 555 | 175580 | break; | |
| 556 | |||
| 557 | 164520 | case 6: | |
| 558 | 164520 | r1 = ExprStack[stackindex]; | |
| 559 | 164520 | stackindex--; | |
| 560 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 164520 times.
|
164520 | if (stackindex < 0) break; |
| 561 | 164520 | r2 = ExprStack[stackindex]; | |
| 562 | 164520 | ExprStack[stackindex] = r2 / r1; | |
| 563 | 164520 | break; | |
| 564 | |||
| 565 | 140042 | case 7: | |
| 566 | 140042 | stackindex++; | |
| 567 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 140042 times.
|
140042 | if (stackindex >= MAX_STACK_SIZE) break; |
| 568 | 140042 | ExprStack[stackindex] = node->fvalue; | |
| 569 | 140042 | break; | |
| 570 | |||
| 571 | 501020 | case 8: | |
| 572 |
1/2✓ Branch 0 taken 501020 times.
✗ Branch 1 not taken.
|
501020 | if (getVariableValue != NULL) |
| 573 | { | ||
| 574 | 501020 | r1 = getVariableValue(node->ivar); | |
| 575 | } | ||
| 576 | ✗ | else r1 = 0.0; | |
| 577 | 501020 | stackindex++; | |
| 578 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 501020 times.
|
501020 | if (stackindex >= MAX_STACK_SIZE) break; |
| 579 | 501020 | ExprStack[stackindex] = r1; | |
| 580 | 501020 | break; | |
| 581 | |||
| 582 | 480 | case 9: | |
| 583 | 480 | ExprStack[stackindex] = -ExprStack[stackindex]; | |
| 584 | 480 | break; | |
| 585 | |||
| 586 | 240 | case 10: | |
| 587 | 240 | r1 = ExprStack[stackindex]; | |
| 588 | 240 | r2 = cos(r1); | |
| 589 | 240 | ExprStack[stackindex] = r2; | |
| 590 | 240 | break; | |
| 591 | |||
| 592 | 240 | case 11: | |
| 593 | 240 | r1 = ExprStack[stackindex]; | |
| 594 | 240 | r2 = sin(r1); | |
| 595 | 240 | ExprStack[stackindex] = r2; | |
| 596 | 240 | break; | |
| 597 | |||
| 598 | 240 | case 12: | |
| 599 | 240 | r1 = ExprStack[stackindex]; | |
| 600 | 240 | r2 = tan(r1); | |
| 601 | 240 | ExprStack[stackindex] = r2; | |
| 602 | 240 | break; | |
| 603 | |||
| 604 | 240 | case 13: | |
| 605 | 240 | r1 = ExprStack[stackindex]; | |
| 606 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 240 times.
|
240 | if (r1 == 0.0) r2 = 0.0; |
| 607 | 240 | else r2 = 1.0/tan( r1 ); | |
| 608 | 240 | ExprStack[stackindex] = r2; | |
| 609 | 240 | break; | |
| 610 | |||
| 611 | 240 | case 14: | |
| 612 | 240 | r1 = ExprStack[stackindex]; | |
| 613 | 240 | r2 = fabs( r1 ); | |
| 614 | 240 | ExprStack[stackindex] = r2; | |
| 615 | 240 | break; | |
| 616 | |||
| 617 | 240 | case 15: | |
| 618 | 240 | r1 = ExprStack[stackindex]; | |
| 619 |
2/2✓ Branch 0 taken 92 times.
✓ Branch 1 taken 148 times.
|
240 | if (r1 < 0.0) r2 = -1.0; |
| 620 |
1/2✓ Branch 0 taken 148 times.
✗ Branch 1 not taken.
|
148 | else if (r1 > 0.0) r2 = 1.0; |
| 621 | ✗ | else r2 = 0.0; | |
| 622 | 240 | ExprStack[stackindex] = r2; | |
| 623 | 240 | break; | |
| 624 | |||
| 625 | 240 | case 16: | |
| 626 | 240 | r1 = ExprStack[stackindex]; | |
| 627 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 240 times.
|
240 | if (r1 < 0.0) r2 = 0.0; |
| 628 | 240 | else r2 = sqrt( r1 ); | |
| 629 | 240 | ExprStack[stackindex] = r2; | |
| 630 | 240 | break; | |
| 631 | |||
| 632 | 240 | case 17: | |
| 633 | 240 | r1 = ExprStack[stackindex]; | |
| 634 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 240 times.
|
240 | if (r1 <= 0) r2 = 0.0; |
| 635 | 240 | else r2 = log(r1); | |
| 636 | 240 | ExprStack[stackindex] = r2; | |
| 637 | 240 | break; | |
| 638 | |||
| 639 | 240 | case 18: | |
| 640 | 240 | r1 = ExprStack[stackindex]; | |
| 641 | 240 | r2 = exp(r1); | |
| 642 | 240 | ExprStack[stackindex] = r2; | |
| 643 | 240 | break; | |
| 644 | |||
| 645 | 240 | case 19: | |
| 646 | 240 | r1 = ExprStack[stackindex]; | |
| 647 | 240 | r2 = asin( r1 ); | |
| 648 | 240 | ExprStack[stackindex] = r2; | |
| 649 | 240 | break; | |
| 650 | |||
| 651 | 240 | case 20: | |
| 652 | 240 | r1 = ExprStack[stackindex]; | |
| 653 | 240 | r2 = acos( r1 ); | |
| 654 | 240 | ExprStack[stackindex] = r2; | |
| 655 | 240 | break; | |
| 656 | |||
| 657 | 240 | case 21: | |
| 658 | 240 | r1 = ExprStack[stackindex]; | |
| 659 | 240 | r2 = atan( r1 ); | |
| 660 | 240 | ExprStack[stackindex] = r2; | |
| 661 | 240 | break; | |
| 662 | |||
| 663 | 240 | case 22: | |
| 664 | 240 | r1 = ExprStack[stackindex]; | |
| 665 | 240 | r2 = 1.57079632679489661923 - atan(r1); | |
| 666 | 240 | ExprStack[stackindex] = r2; | |
| 667 | 240 | break; | |
| 668 | |||
| 669 | 240 | case 23: | |
| 670 | 240 | r1 = ExprStack[stackindex]; | |
| 671 | 240 | r2 = (exp(r1)-exp(-r1))/2.0; | |
| 672 | 240 | ExprStack[stackindex] = r2; | |
| 673 | 240 | break; | |
| 674 | |||
| 675 | 240 | case 24: | |
| 676 | 240 | r1 = ExprStack[stackindex]; | |
| 677 | 240 | r2 = (exp(r1)+exp(-r1))/2.0; | |
| 678 | 240 | ExprStack[stackindex] = r2; | |
| 679 | 240 | break; | |
| 680 | |||
| 681 | 240 | case 25: | |
| 682 | 240 | r1 = ExprStack[stackindex]; | |
| 683 | 240 | r2 = (exp(r1)-exp(-r1))/(exp(r1)+exp(-r1)); | |
| 684 | 240 | ExprStack[stackindex] = r2; | |
| 685 | 240 | break; | |
| 686 | |||
| 687 | 240 | case 26: | |
| 688 | 240 | r1 = ExprStack[stackindex]; | |
| 689 | 240 | r2 = (exp(r1)+exp(-r1))/(exp(r1)-exp(-r1)); | |
| 690 | 240 | ExprStack[stackindex] = r2; | |
| 691 | 240 | break; | |
| 692 | |||
| 693 | 240 | case 27: | |
| 694 | 240 | r1 = ExprStack[stackindex]; | |
| 695 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 240 times.
|
240 | if (r1 == 0.0) r2 = 0.0; |
| 696 | 240 | else r2 = log10( r1 ); | |
| 697 | 240 | ExprStack[stackindex] = r2; | |
| 698 | 240 | break; | |
| 699 | |||
| 700 | 240 | case 28: | |
| 701 | 240 | r1 = ExprStack[stackindex]; | |
| 702 |
2/2✓ Branch 0 taken 109 times.
✓ Branch 1 taken 131 times.
|
240 | if (r1 <= 0.0) r2 = 0.0; |
| 703 | 131 | else r2 = 1.0; | |
| 704 | 240 | ExprStack[stackindex] = r2; | |
| 705 | 240 | break; | |
| 706 | |||
| 707 | 1300 | case 31: | |
| 708 | 1300 | r1 = ExprStack[stackindex]; | |
| 709 | 1300 | stackindex--; | |
| 710 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1300 times.
|
1300 | if (stackindex < 0) break; |
| 711 | 1300 | r2 = ExprStack[stackindex]; | |
| 712 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1298 times.
|
1300 | if (r2 <= 0.0) r2 = 0.0; |
| 713 | 1298 | else r2 = pow(r2, r1); | |
| 714 | 1300 | ExprStack[stackindex] = r2; | |
| 715 | 1300 | break; | |
| 716 | } | ||
| 717 | 997352 | node = node->next; | |
| 718 | } | ||
| 719 |
1/2✓ Branch 0 taken 289812 times.
✗ Branch 1 not taken.
|
289812 | if (stackindex >= 0) |
| 720 | 289812 | r1 = ExprStack[stackindex]; | |
| 721 | else | ||
| 722 | ✗ | r1 = 0.0; | |
| 723 | |||
| 724 | // Set result to 0 if it is NaN due to an illegal math op | ||
| 725 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 289812 times.
|
289812 | if ( r1 != r1 ) r1 = 0.0; |
| 726 | |||
| 727 | 289812 | return r1; | |
| 728 | } | ||
| 729 | |||
| 730 | // Turn off "precise" floating point option | ||
| 731 | #pragma float_control(pop) | ||
| 732 | |||
| 733 | //============================================================================= | ||
| 734 | |||
| 735 | 5080 | void mathexpr_delete(MathExpr *expr) | |
| 736 | { | ||
| 737 |
2/2✓ Branch 0 taken 258 times.
✓ Branch 1 taken 4822 times.
|
5080 | if (expr) mathexpr_delete(expr->next); |
| 738 | 5080 | free(expr); | |
| 739 | 5080 | } | |
| 740 | |||
| 741 | //============================================================================= | ||
| 742 | |||
| 743 | 29 | MathExpr * mathexpr_create(char *formula, int (*getVar) (char *)) | |
| 744 | { | ||
| 745 | ExprTree *tree; | ||
| 746 | 29 | MathExpr *expr = NULL; | |
| 747 | 29 | MathExpr *result = NULL; | |
| 748 | 29 | getVariableIndex = getVar; | |
| 749 | 29 | Err = 0; | |
| 750 | 29 | PrevLex = 0; | |
| 751 | 29 | CurLex = 0; | |
| 752 | 29 | S = formula; | |
| 753 | 29 | Len = (int)strlen(S); | |
| 754 | 29 | Pos = 0; | |
| 755 | 29 | Bc = 0; | |
| 756 | 29 | tree = getTree(); | |
| 757 |
2/4✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
|
29 | if (Bc == 0 && Err == 0) |
| 758 | { | ||
| 759 | 29 | traverseTree(tree, &expr); | |
| 760 |
2/2✓ Branch 0 taken 258 times.
✓ Branch 1 taken 29 times.
|
287 | while (expr) |
| 761 | { | ||
| 762 | 258 | result = expr; | |
| 763 | 258 | expr = expr->prev; | |
| 764 | } | ||
| 765 | } | ||
| 766 | 29 | deleteTree(tree); | |
| 767 | 29 | return result; | |
| 768 | } | ||
| 769 |