1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: UnitTestFunctionParser.cxx
5
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE. See the above copyright notice for more information.
13
14 =========================================================================*/
15
16 #include "vtkSmartPointer.h"
17
18 #include "vtkFunctionParser.h"
19
20 #include "vtkMathUtilities.h"
21 #include "vtkMinimalStandardRandomSequence.h"
22 #include "vtkTestErrorObserver.h"
23
24 #include <algorithm>
25 #include <sstream>
26 #include <string>
27 #include <vector>
28
29 constexpr bool STATUS_SUCCESS = true;
30 constexpr bool STATUS_FAILURE = false;
31
32 #define SCALAR_FUNC(proc, function, math) \
33 static bool proc(double low, double hi) \
34 { \
35 std::cout << "Testing " << #function << "..."; \
36 auto parser = vtkSmartPointer<vtkFunctionParser>::New(); \
37 std::string _fun(#function); \
38 _fun += "(x)"; \
39 parser->SetFunction(_fun.c_str()); \
40 \
41 auto rand = vtkSmartPointer<vtkMinimalStandardRandomSequence>::New(); \
42 for (unsigned int i = 0; i < 1000; ++i) \
43 { \
44 double value = rand->GetNextRangeValue(low, hi); \
45 parser->SetScalarVariableValue("x", value); \
46 double result = parser->GetScalarResult(); \
47 double expected = math(value); \
48 if (!vtkMathUtilities::FuzzyCompare( \
49 result, expected, std::numeric_limits<double>::epsilon() * 1.0)) \
50 { \
51 std::cout << "\n" #function " Expected " << expected << " but got " << result \
52 << " difference is " << result - expected << " eps ratio is: " \
53 << (result - expected) / std::numeric_limits<double>::epsilon() << std::endl; \
54 return STATUS_FAILURE; \
55 } \
56 } \
57 \
58 std::cout << "PASSED\n"; \
59 return STATUS_SUCCESS; \
60 }
61
62 SCALAR_FUNC(TestAbs, abs, std::abs);
63 SCALAR_FUNC(TestAcos, acos, std::acos);
64 SCALAR_FUNC(TestAsin, asin, std::asin);
65 SCALAR_FUNC(TestAtan, atan, std::atan);
66 SCALAR_FUNC(TestCeil, ceil, std::ceil);
67 SCALAR_FUNC(TestCos, cos, std::cos);
68 SCALAR_FUNC(TestCosh, cosh, std::cosh);
69 SCALAR_FUNC(TestExp, exp, std::exp);
70 SCALAR_FUNC(TestFloor, floor, std::floor);
71 SCALAR_FUNC(TestLn, ln, std::log);
72 SCALAR_FUNC(TestLog10, log10, std::log10);
73 SCALAR_FUNC(TestSin, sin, std::sin);
74 SCALAR_FUNC(TestSinh, sinh, std::sinh);
75 SCALAR_FUNC(TestSqrt, sqrt, std::sqrt);
76 SCALAR_FUNC(TestTan, tan, std::tan);
77 SCALAR_FUNC(TestTanh, tanh, std::tanh);
78 static bool TestScalars();
79 static bool TestVariableNames();
80 static bool TestSpacing();
81 static bool TestUnaryOperations();
82 static bool TestScientificNotation();
83 static bool TestVectors();
84 static bool TestMinMax();
85 static bool TestScalarLogic();
86 static bool TestVectorLogic();
87 static bool TestMiscFunctions();
88 static bool TestErrors();
89
UnitTestFunctionParser(int,char * [])90 int UnitTestFunctionParser(int, char*[])
91 {
92 bool status = STATUS_SUCCESS;
93
94 status &= TestAbs(-1000.0, 1000);
95 status &= TestAcos(-1.0, 1.0);
96 status &= TestAsin(-1.0, 1.0);
97 status &= TestAtan(-1.0, 1.0);
98 status &= TestCeil(-1000.0, 1000.0);
99 status &= TestCos(-1000.0, 1000.0);
100 status &= TestCosh(-1.0, 1.0);
101 status &= TestExp(0, 2.0);
102 status &= TestFloor(-1000.0, 1000.0);
103 status &= TestLn(0.0, 1000.0);
104 status &= TestLog10(0.0, 1000.0);
105 status &= TestSin(-1000.0, 1000.0);
106 status &= TestSinh(-1.0, 1.0);
107 status &= TestSqrt(.1, 1000.0);
108 status &= TestTan(-1000.0, 1000.0);
109 status &= TestTanh(-1.0, 1.0);
110
111 status &= TestScalars();
112 status &= TestVariableNames();
113 status &= TestSpacing();
114 status &= TestUnaryOperations();
115 status &= TestScientificNotation();
116 status &= TestVectors();
117 status &= TestMinMax();
118 status &= TestScalarLogic();
119 status &= TestVectorLogic();
120
121 status &= TestMiscFunctions();
122 status &= TestErrors();
123 if (status == STATUS_FAILURE)
124 {
125 return EXIT_FAILURE;
126 }
127
128 // Test printing of an uninitialized parser
129 std::ostringstream functionPrint;
130 auto functionParser = vtkSmartPointer<vtkFunctionParser>::New();
131 functionParser->Print(functionPrint);
132
133 return EXIT_SUCCESS;
134 }
135
TestUnaryOperations()136 bool TestUnaryOperations()
137 {
138 std::cout << "Testing Scalar Unary"
139 << "...";
140 std::string formula[4] = { "-x * +y", "+x + +y", "+x - -y", "-x - +y" };
141 double expected[4] = { -2., 3., 3., -3. };
142
143 auto parser = vtkSmartPointer<vtkFunctionParser>::New();
144 parser->SetScalarVariableValue("x", 1.0);
145 parser->SetScalarVariableValue("y", 2.0);
146 for (unsigned i = 0; i < 4; i++)
147 {
148 parser->SetFunction(&formula[i][0]);
149 double result = parser->GetScalarResult();
150 if (!vtkMathUtilities::FuzzyCompare(
151 result, expected[i], std::numeric_limits<double>::epsilon() * 1.0))
152 {
153 std::cout << "FAILED\n";
154 return STATUS_FAILURE;
155 }
156 }
157
158 parser->SetScalarVariableValue("x", 3);
159 parser->SetScalarVariableValue("y", 2);
160 parser->SetFunction("(-x) ^ +y");
161 int result = parser->GetScalarResult();
162 if (result != 9)
163 {
164 std::cout << "FAILED\n";
165 return STATUS_FAILURE;
166 }
167
168 parser->SetFunction("(-x)");
169 result = parser->GetScalarResult();
170 if (result != -3)
171 {
172 std::cout << "FAILED\n";
173 return STATUS_FAILURE;
174 }
175
176 std::cout << "PASSED\n";
177 return STATUS_SUCCESS;
178 }
179
TestScalars()180 bool TestScalars()
181 {
182 std::cout << "Testing Scalar Add / Subtract / Multiply / Divide"
183 << "...";
184 auto parser = vtkSmartPointer<vtkFunctionParser>::New();
185 parser->SetScalarVariableValue("x", 1.0);
186 parser->SetScalarVariableValue("y", 2.0);
187 parser->SetFunction("+(x-y)/(x-y) * -(x-y)/(x-y) + (x - x)");
188 double result = parser->GetScalarResult();
189 if (result != -1.0)
190 {
191 std::cout << "FAILED\n";
192 return STATUS_FAILURE;
193 }
194 else
195 {
196 std::cout << "PASSED\n";
197 return STATUS_SUCCESS;
198 }
199 }
200
TestVariableNames()201 bool TestVariableNames()
202 {
203 std::cout << "Testing variable names similar to math ops with parentheses "
204 << "...";
205 auto parser = vtkSmartPointer<vtkFunctionParser>::New();
206 parser->SetScalarVariableValue("absolutex", 1.0);
207 parser->SetScalarVariableValue("y", 2.0);
208 parser->SetFunction("absolutex - (y)");
209 double result = parser->GetScalarResult();
210 if (result != -1.0)
211 {
212 std::cout << "FAILED\n";
213 return STATUS_FAILURE;
214 }
215 else
216 {
217 std::cout << "PASSED\n";
218 return STATUS_SUCCESS;
219 }
220 }
221
TestSpacing()222 bool TestSpacing()
223 {
224 std::cout << "Testing spacing with math ops "
225 << "...";
226 auto parser = vtkSmartPointer<vtkFunctionParser>::New();
227 parser->SetScalarVariableValue("x", -1.0);
228 parser->SetFunction("abs(x)");
229 double result = parser->GetScalarResult();
230 if (result != 1.0)
231 {
232 std::cout << "FAILED\n";
233 return STATUS_FAILURE;
234 }
235 parser->SetFunction("abs (x)");
236 result = parser->GetScalarResult();
237 if (result != 1.0)
238 {
239 std::cout << "FAILED\n";
240 return STATUS_FAILURE;
241 }
242 else
243 {
244 std::cout << "PASSED\n";
245 return STATUS_SUCCESS;
246 }
247 }
248
TestScientificNotation()249 bool TestScientificNotation()
250 {
251 std::cout << "Testing Scientific notation"
252 << "...";
253 auto parser = vtkSmartPointer<vtkFunctionParser>::New();
254 parser->SetFunction("3.0e+01");
255 double expected = 3.0e+01;
256 double result = parser->GetScalarResult();
257 if (!vtkMathUtilities::FuzzyCompare(
258 result, expected, std::numeric_limits<double>::epsilon() * 1.0))
259 {
260 std::cout << " Scientific notation expected " << expected << " but got " << result;
261 std::cout << "eps ratio is: " << (result - expected) / std::numeric_limits<double>::epsilon()
262 << std::endl;
263 std::cout << "FAILED\n";
264 return STATUS_FAILURE;
265 }
266 else
267 {
268 std::cout << "PASSED\n";
269 return STATUS_SUCCESS;
270 }
271 }
272
TestVectors()273 bool TestVectors()
274 {
275 std::cout << "Testing Cross"
276 << "...";
277 auto parser = vtkSmartPointer<vtkFunctionParser>::New();
278
279 bool status1 = STATUS_SUCCESS;
280 bool status2 = STATUS_SUCCESS;
281 bool status3 = STATUS_SUCCESS;
282 bool status4 = STATUS_SUCCESS;
283 bool status5 = STATUS_SUCCESS;
284
285 auto rand = vtkSmartPointer<vtkMinimalStandardRandomSequence>::New();
286 // Cross
287 for (unsigned int i = 0; i < 10; ++i)
288 {
289 double x0 = rand->GetNextRangeValue(-1.0, 1.0);
290 double x1 = rand->GetNextRangeValue(-1.0, 1.0);
291 double x2 = rand->GetNextRangeValue(-1.0, 1.0);
292 parser->SetVectorVariableValue("x", x0, x1, x2);
293
294 double y0 = rand->GetNextRangeValue(-1.0, 1.0);
295 double y1 = rand->GetNextRangeValue(-1.0, 1.0);
296 double y2 = rand->GetNextRangeValue(-1.0, 1.0);
297 parser->SetVectorVariableValue("y", y0, y1, y2);
298
299 parser->SetFunction("cross(x,y)");
300 double* result = parser->GetVectorResult();
301 double axb[3];
302 axb[0] = result[0];
303 axb[1] = result[1];
304 axb[2] = result[2];
305 // repeat to cover a 0 return from Evaluate()
306 parser->IsVectorResult();
307 parser->IsVectorResult();
308
309 parser->SetFunction("cross(-y,x)");
310 result = parser->GetVectorResult();
311 double minusBxa[3];
312 minusBxa[0] = result[0];
313 minusBxa[1] = result[1];
314 minusBxa[2] = result[2];
315
316 // a x b = -b x a
317 for (int j = 0; j < 3; ++j)
318 {
319 if (!vtkMathUtilities::FuzzyCompare(
320 axb[j], minusBxa[j], std::numeric_limits<double>::epsilon() * 1.0))
321 {
322 std::cout << " Cross expected " << minusBxa[j] << " but got " << axb[j];
323 std::cout << "eps ratio is: "
324 << (axb[j] - minusBxa[j]) / std::numeric_limits<double>::epsilon() << std::endl;
325 status1 = STATUS_FAILURE;
326 }
327 }
328 }
329 if (status1 == STATUS_SUCCESS)
330 {
331 std::cout << "PASSED\n";
332 }
333 else
334 {
335 std::cout << "FAILED\n";
336 }
337
338 parser->RemoveAllVariables();
339 // Add / Subtract / Multiply / Unary / Dot / Mag / Norm
340 std::cout << "Testing Add / Subtract / Multiply / Unary / Dot"
341 << "...";
342 for (unsigned int i = 0; i < 10; ++i)
343 {
344 double x0 = rand->GetNextRangeValue(-1.0, 1.0);
345 double x1 = rand->GetNextRangeValue(-1.0, 1.0);
346 double x2 = rand->GetNextRangeValue(-1.0, 1.0);
347 parser->SetVectorVariableValue("x", x0, x1, x2);
348
349 double y0 = rand->GetNextRangeValue(-1.0, 1.0);
350 double y1 = rand->GetNextRangeValue(-1.0, 1.0);
351 double y2 = rand->GetNextRangeValue(-1.0, 1.0);
352 parser->SetVectorVariableValue("y", y0, y1, y2);
353
354 parser->SetScalarVariableValue("t", 2.0);
355 parser->SetFunction("t*(x + y - (x + y))/t");
356 double* result = parser->GetVectorResult();
357 double a[3];
358 a[0] = result[0];
359 a[1] = result[1];
360 a[2] = result[2];
361
362 parser->SetScalarVariableValue("s", 0.0);
363 parser->SetFunction("x * s");
364 result = parser->GetVectorResult();
365 double b[3];
366 b[0] = result[0];
367 b[1] = result[1];
368 b[2] = result[2];
369
370 // 2.0 * ((x + y - (x + y)) / 2.0 = x * 0.0
371 for (int j = 0; j < 3; ++j)
372 {
373 if (!vtkMathUtilities::FuzzyCompare(a[j], b[j], std::numeric_limits<double>::epsilon() * 1.0))
374 {
375 std::cout << " Cross expected " << a[j] << " but got " << b[j];
376 std::cout << "eps ratio is: " << (a[j] - b[j]) / std::numeric_limits<double>::epsilon()
377 << std::endl;
378 status2 = STATUS_FAILURE;
379 }
380 }
381 // Test Dot / Mag / Norm
382 // a x b dot a = 0
383 parser->SetFunction("cross(x, y).x");
384 double dot = parser->GetScalarResult();
385 if (!vtkMathUtilities::FuzzyCompare(dot, 0.0, std::numeric_limits<double>::epsilon() * 1.0))
386 {
387 std::cout << " Dot " << 0.0 << " but got " << dot;
388 std::cout << "eps ratio is: " << (dot - 0.0) / std::numeric_limits<double>::epsilon()
389 << std::endl;
390 status3 = STATUS_FAILURE;
391 }
392
393 // Test Mag and Norm
394 // mag(norm(x) == 1
395 parser->SetFunction("mag(norm(x))");
396 double mag = parser->GetScalarResult();
397 if (!vtkMathUtilities::FuzzyCompare(mag, 1.0, std::numeric_limits<double>::epsilon() * 2.0))
398 {
399 std::cout << " Mag expected" << 1.0 << " but got " << mag;
400 std::cout << " eps ratio is: " << (mag - 1.0) / std::numeric_limits<double>::epsilon()
401 << std::endl;
402 status4 = STATUS_FAILURE;
403 }
404 }
405
406 parser->RemoveAllVariables();
407 // x *iHat + y * jHat + z * zHat
408 parser->SetScalarVariableValue("x", 1.0);
409 parser->SetScalarVariableValue("y", 2.0);
410 parser->SetScalarVariableValue("z", 3.0);
411 parser->SetFunction("x*iHat + y*jHat + z*kHat");
412 double* xyz = parser->GetVectorResult();
413 if (xyz[0] != 1.0 || xyz[1] != 2.0 || xyz[2] != 3.0)
414 {
415 std::cout << "x*iHat + y*jHat + z*kHat expected "
416 << "(" << 1.0 << "," << 2.0 << "," << 3.0 << ") but got "
417 << "(" << xyz[0] << "," << xyz[1] << "," << xyz[2] << ")" << std::endl;
418 status5 = STATUS_FAILURE;
419 }
420
421 // Test printing of an initialized parser
422 std::ostringstream parserPrint;
423 parser->Print(parserPrint);
424
425 // Now clear the variables
426 parser->RemoveAllVariables();
427 if (parser->GetNumberOfScalarVariables() != 0 || parser->GetNumberOfVectorVariables() != 0)
428 {
429 std::cout << "RemoveAllVariables failed" << std::endl;
430 status1 = STATUS_FAILURE;
431 }
432
433 // Invalidate function should change the function's mtime
434 vtkMTimeType before = parser->GetMTime();
435 parser->InvalidateFunction();
436 vtkMTimeType after = parser->GetMTime();
437
438 if (before >= after)
439 {
440 std::cout << "InvalidateFunction() failed. MTime should have been modified" << std::endl;
441 status5 = STATUS_FAILURE;
442 }
443
444 bool statusAll = status1 && status2 && status3 & status4 && status5;
445 if (statusAll == STATUS_SUCCESS)
446 {
447 std::cout << "PASSED\n";
448 }
449 return statusAll;
450 }
451
TestMinMax()452 bool TestMinMax()
453 {
454 std::cout << "Testing Min/Max"
455 << "...";
456 auto parser = vtkSmartPointer<vtkFunctionParser>::New();
457
458 parser->SetFunction("min(x,y)");
459
460 auto rand = vtkSmartPointer<vtkMinimalStandardRandomSequence>::New();
461 bool status = STATUS_SUCCESS;
462 for (unsigned int i = 0; i < 1000; ++i)
463 {
464 double value = rand->GetNextRangeValue(-1000.0, 1000.0);
465 parser->SetScalarVariableValue("x", value);
466 parser->SetScalarVariableValue("y", -value);
467
468 double result = parser->GetScalarResult();
469 double expected = std::min(value, -value);
470 if (!vtkMathUtilities::FuzzyCompare(
471 result, expected, std::numeric_limits<double>::epsilon() * 1.0))
472 {
473 std::cout << "\n";
474 std::cout << "Min Expected " << expected << " but got " << result << " difference is "
475 << result - expected << " ";
476 std::cout << "eps ratio is: " << (result - expected) / std::numeric_limits<double>::epsilon()
477 << std::endl;
478 status = STATUS_FAILURE;
479 }
480 }
481
482 parser->SetFunction("max(x,y)");
483
484 for (unsigned int i = 0; i < 1000; ++i)
485 {
486 double value = rand->GetNextRangeValue(-1000.0, 1000.0);
487 parser->SetScalarVariableValue("x", value);
488 parser->SetScalarVariableValue("y", -value);
489
490 double result = parser->GetScalarResult();
491 double expected = std::max(value, -value);
492 if (!vtkMathUtilities::FuzzyCompare(
493 result, expected, std::numeric_limits<double>::epsilon() * 1.0))
494 {
495 std::cout << "\n";
496 std::cout << "Max Expected " << expected << " but got " << result << " difference is "
497 << result - expected << " ";
498 std::cout << "eps ratio is: " << (result - expected) / std::numeric_limits<double>::epsilon()
499 << std::endl;
500 status = STATUS_FAILURE;
501 }
502 }
503
504 if (status == STATUS_SUCCESS)
505 {
506 std::cout << "PASSED\n";
507 }
508 return status;
509 }
510
TestScalarLogic()511 bool TestScalarLogic()
512 {
513 bool status = STATUS_SUCCESS;
514 auto rand = vtkSmartPointer<vtkMinimalStandardRandomSequence>::New();
515
516 std::cout << "Testing Scalar Logic"
517 << "...";
518 auto parser = vtkSmartPointer<vtkFunctionParser>::New();
519
520 parser->SetFunction("if(x < y, x, y)");
521 for (unsigned int i = 0; i < 1000; ++i)
522 {
523 double x = rand->GetNextRangeValue(-1000.0, 1000.0);
524 double y = rand->GetNextRangeValue(-1000.0, 1000.0);
525 parser->SetScalarVariableValue("x", x);
526 parser->SetScalarVariableValue("y", y);
527
528 double result = parser->GetScalarResult();
529 double expected = x < y ? x : y;
530 if (result != expected)
531 {
532 std::cout << "\n";
533 std::cout << x << " < " << y << " Expected " << expected << " but got " << result
534 << std::endl;
535 status = STATUS_FAILURE;
536 }
537 }
538
539 parser->SetFunction("if(x > y, x, y)");
540 for (unsigned int i = 0; i < 1000; ++i)
541 {
542 double x = rand->GetNextRangeValue(-1000.0, 1000.0);
543 double y = rand->GetNextRangeValue(-1000.0, 1000.0);
544 parser->SetScalarVariableValue("x", x);
545 parser->SetScalarVariableValue("y", y);
546
547 double result = parser->GetScalarResult();
548 double expected = x > y ? x : y;
549 if (result != expected)
550 {
551 std::cout << "\n";
552 std::cout << x << " > " << y << " Expected " << expected << " but got " << result
553 << std::endl;
554 status = STATUS_FAILURE;
555 }
556 }
557
558 parser->SetFunction("if(x = y, x, 0.0)");
559 for (unsigned int i = 0; i < 1000; ++i)
560 {
561 double x = rand->GetNextRangeValue(-1000.0, 1000.0);
562 double y = x;
563 parser->SetScalarVariableValue("x", x);
564 parser->SetScalarVariableValue("y", y);
565
566 double result = parser->GetScalarResult();
567 double expected = x == y ? x : 0.0;
568 if (result != expected)
569 {
570 std::cout << "\n";
571 std::cout << x << " == " << y << " Expected " << expected << " but got " << result
572 << std::endl;
573 status = STATUS_FAILURE;
574 }
575 }
576
577 double ii[] = { 0.0, 0.0, 1.0, 1.0 };
578 double jj[] = { 0.0, 1.0, 0.0, 1.0 };
579 double expectedOr[] = { 0.0, 1.0, 1.0, 1.0 };
580 double expectedAnd[] = { .0, 0.0, 0.0, 1.0 };
581
582 parser->SetFunction("i | j");
583 for (int i = 0; i < 3; ++i)
584 {
585 parser->SetScalarVariableValue("i", ii[i]);
586 parser->SetScalarVariableValue("j", jj[i]);
587 double result = parser->GetScalarResult();
588 if (result != expectedOr[i])
589 {
590 std::cout << "i | j expected " << expectedOr[i] << " but got " << result << std::endl;
591 status = STATUS_FAILURE;
592 }
593 }
594
595 parser->SetFunction("i & j");
596 for (int i = 0; i < 3; ++i)
597 {
598 parser->SetScalarVariableValue("i", ii[i]);
599 parser->SetScalarVariableValue("j", jj[i]);
600 double result = parser->GetScalarResult();
601 if (result != expectedAnd[i])
602 {
603 std::cout << "i | j expected " << expectedAnd[i] << " but got " << result << std::endl;
604 status = STATUS_FAILURE;
605 }
606 }
607
608 if (status == STATUS_SUCCESS)
609 {
610 std::cout << "PASSED\n";
611 }
612 else
613 {
614 std::cout << "FAILED\n";
615 }
616 return status;
617 }
618
TestVectorLogic()619 bool TestVectorLogic()
620 {
621 bool status = STATUS_SUCCESS;
622 auto rand = vtkSmartPointer<vtkMinimalStandardRandomSequence>::New();
623
624 std::cout << "Testing Vector Logic"
625 << "...";
626 auto parser = vtkSmartPointer<vtkFunctionParser>::New();
627
628 parser->SetFunction("if(x < y, v, w)");
629 for (unsigned int i = 0; i < 1000; ++i)
630 {
631 double x = rand->GetNextRangeValue(-1000.0, 1000.0);
632 double y = rand->GetNextRangeValue(-1000.0, 1000.0);
633 parser->SetScalarVariableValue("x", x);
634 parser->SetScalarVariableValue("y", y);
635
636 double v1 = rand->GetNextRangeValue(-1000.0, 1000.0);
637 double v2 = rand->GetNextRangeValue(-1000.0, 1000.0);
638 double v3 = rand->GetNextRangeValue(-1000.0, 1000.0);
639 double w1 = rand->GetNextRangeValue(-1000.0, 1000.0);
640 double w2 = rand->GetNextRangeValue(-1000.0, 1000.0);
641 double w3 = rand->GetNextRangeValue(-1000.0, 1000.0);
642 parser->SetVectorVariableValue("v", v1, v2, v3);
643 parser->SetVectorVariableValue("w", w1, w2, w3);
644
645 double result = parser->GetVectorResult()[0];
646 double expected = x < y ? v1 : w1;
647 if (result != expected)
648 {
649 std::cout << "\n";
650 std::cout << x << " < " << y << " Expected " << expected << " but got " << result
651 << std::endl;
652 status = STATUS_FAILURE;
653 }
654 }
655
656 parser->SetFunction("if(x > y, v, w)");
657 for (unsigned int i = 0; i < 1000; ++i)
658 {
659 double x = rand->GetNextRangeValue(-1000.0, 1000.0);
660 double y = rand->GetNextRangeValue(-1000.0, 1000.0);
661 parser->SetScalarVariableValue("x", x);
662 parser->SetScalarVariableValue("y", y);
663
664 double v1 = rand->GetNextRangeValue(-1000.0, 1000.0);
665 double v2 = rand->GetNextRangeValue(-1000.0, 1000.0);
666 double v3 = rand->GetNextRangeValue(-1000.0, 1000.0);
667 double w1 = rand->GetNextRangeValue(-1000.0, 1000.0);
668 double w2 = rand->GetNextRangeValue(-1000.0, 1000.0);
669 double w3 = rand->GetNextRangeValue(-1000.0, 1000.0);
670 parser->SetVectorVariableValue("v", v1, v2, v3);
671 parser->SetVectorVariableValue("w", w1, w2, w3);
672
673 double result = parser->GetVectorResult()[0];
674 double expected = x > y ? v1 : w1;
675 if (result != expected)
676 {
677 std::cout << "\n";
678 std::cout << x << " > " << y << " Expected " << expected << " but got " << result
679 << std::endl;
680 status = STATUS_FAILURE;
681 }
682 }
683
684 parser->SetFunction("if(x = y, w, v * 0.0)");
685 for (unsigned int i = 0; i < 1000; ++i)
686 {
687
688 double x = rand->GetNextRangeValue(-1000.0, 1000.0);
689 double y = x;
690 parser->SetScalarVariableValue("x", x);
691 parser->SetScalarVariableValue("y", y);
692
693 double v1 = rand->GetNextRangeValue(-1000.0, 1000.0);
694 double v2 = rand->GetNextRangeValue(-1000.0, 1000.0);
695 double v3 = rand->GetNextRangeValue(-1000.0, 1000.0);
696 double w1 = rand->GetNextRangeValue(-1000.0, 1000.0);
697 double w2 = rand->GetNextRangeValue(-1000.0, 1000.0);
698 double w3 = rand->GetNextRangeValue(-1000.0, 1000.0);
699 parser->SetVectorVariableValue("v", v1, v2, v3);
700 parser->SetVectorVariableValue("w", w1, w2, w3);
701
702 double result = parser->GetVectorResult()[0];
703 double expected = x > y ? v1 : w1;
704 if (result != expected)
705 {
706 std::cout << "\n";
707 std::cout << x << " == " << y << " Expected " << expected << " but got " << result
708 << std::endl;
709 status = STATUS_FAILURE;
710 }
711 }
712
713 if (status == STATUS_SUCCESS)
714 {
715 std::cout << "PASSED\n";
716 }
717 else
718 {
719 std::cout << "FAILED\n";
720 }
721
722 return status;
723 }
724
TestMiscFunctions()725 bool TestMiscFunctions()
726 {
727 bool statusAll = STATUS_SUCCESS;
728 auto rand = vtkSmartPointer<vtkMinimalStandardRandomSequence>::New();
729
730 std::cout << "Testing Sign"
731 << "...";
732 auto parser = vtkSmartPointer<vtkFunctionParser>::New();
733 parser->SetFunction("sign(x)");
734 double values[3] = { -100.0, 0.0, 100.0 };
735 double expecteds[3] = { -1.0, 0.0, 1.0 };
736
737 bool status = STATUS_SUCCESS;
738 for (unsigned int i = 0; i < 3; ++i)
739 {
740 parser->SetScalarVariableValue("x", values[i]);
741 double result = parser->GetScalarResult();
742 if (result != expecteds[i])
743 {
744 std::cout << "Sign expected " << expecteds[i] << " but got " << result << ". ";
745 status = STATUS_FAILURE;
746 }
747 }
748
749 if (status == STATUS_SUCCESS)
750 {
751 std::cout << "PASSED\n";
752 }
753 else
754 {
755 statusAll = STATUS_FAILURE;
756 std::cout << "FAILED\n";
757 }
758
759 std::cout << "Testing Pow"
760 << "...";
761 status = STATUS_SUCCESS;
762 for (unsigned int i = 0; i < 1000; ++i)
763 {
764 double x = rand->GetNextRangeValue(0.0, 10.0);
765 double y = rand->GetNextRangeValue(0.0, 2.0);
766 parser->SetScalarVariableValue("x", x);
767 parser->SetScalarVariableValue("y", y);
768 parser->SetFunction("x ^ y");
769 double result = parser->GetScalarResult();
770 double expected = std::pow(x, y);
771 if (!vtkMathUtilities::FuzzyCompare(
772 result, expected, std::numeric_limits<double>::epsilon() * 128.0))
773 {
774 std::cout << "\n";
775 std::cout << " pow Expected " << expected << " but got " << result << " difference is "
776 << result - expected << " ";
777 std::cout << "eps ratio is: " << (result - expected) / std::numeric_limits<double>::epsilon()
778 << std::endl;
779 status = STATUS_FAILURE;
780 }
781 }
782 if (status == STATUS_SUCCESS)
783 {
784 std::cout << "PASSED\n";
785 }
786 else
787 {
788 statusAll = STATUS_FAILURE;
789 std::cout << "FAILED\n";
790 }
791
792 std::cout << "Testing Scalar divide"
793 << "...";
794 status = STATUS_SUCCESS;
795 for (unsigned int i = 0; i < 1000; ++i)
796 {
797 double x = rand->GetNextRangeValue(-10.0, 10.0);
798 double y = rand->GetNextRangeValue(-10.0, 10.0);
799 parser->SetScalarVariableValue("x", x);
800 parser->SetScalarVariableValue("y", y);
801 parser->SetFunction("x / y");
802 double result = parser->GetScalarResult();
803 double expected = x / y;
804 if (!vtkMathUtilities::FuzzyCompare(
805 result, expected, std::numeric_limits<double>::epsilon() * 256.0))
806 {
807 std::cout << "\n";
808 std::cout << " x / y Expected " << expected << " but got " << result << " difference is "
809 << result - expected << " ";
810 std::cout << "eps ratio is: " << (result - expected) / std::numeric_limits<double>::epsilon()
811 << std::endl;
812 status = STATUS_FAILURE;
813 }
814 }
815 if (status == STATUS_SUCCESS)
816 {
817 std::cout << "PASSED\n";
818 }
819 else
820 {
821 statusAll = STATUS_FAILURE;
822 std::cout << "FAILED\n";
823 }
824
825 // SetScalarVariableValue
826 std::cout << "Testing SetScalarVariableValue...";
827 parser->SetScalarVariableValue(parser->GetScalarVariableName(0), 123.456);
828 if (parser->GetScalarVariableValue(parser->GetScalarVariableName(0)) != 123.456)
829 {
830 statusAll = STATUS_FAILURE;
831 std::cout << "FAILED\n";
832 }
833 else
834 {
835 std::cout << "PASSED\n";
836 }
837 parser->SetScalarVariableValue(0, 123.45);
838 parser->GetScalarVariableValue("x");
839
840 parser->SetVectorVariableValue("v1", 1.0, 2.0, 3.0);
841 parser->SetVectorVariableValue("v1", 1.0, 1.0, 3.0);
842 parser->SetVectorVariableValue("v1", 1.0, 1.0, 1.0);
843 parser->SetVectorVariableValue(0, 1.0, 2.0, 3.0);
844 parser->SetVectorVariableValue(0, 1.0, 1.0, 3.0);
845 parser->SetVectorVariableValue(0, 1.0, 1.0, 1.0);
846 parser->GetVectorVariableValue(parser->GetVectorVariableName(0));
847 parser->GetVectorVariableName(1000);
848
849 // test functions that can use ReplaceInvalidValue
850 std::vector<std::string> testFuncs;
851 testFuncs.emplace_back("sqrt(s)");
852 testFuncs.emplace_back("ln(s)");
853 testFuncs.emplace_back("log10(s)");
854 testFuncs.emplace_back("asin(s)");
855 testFuncs.emplace_back("acos(s)");
856 testFuncs.emplace_back("s/zero");
857
858 parser->ReplaceInvalidValuesOn();
859 parser->SetReplacementValue(1234.5);
860 parser->SetScalarVariableValue("s", -1000.0);
861 parser->SetScalarVariableValue("zero", 0.0);
862
863 for (size_t f = 0; f < testFuncs.size(); ++f)
864 {
865 parser->SetFunction(testFuncs[f].c_str());
866 if (parser->GetScalarResult() != 1234.5)
867 {
868 std::cout << testFuncs[f]
869 << " failed to return a replacement value when ReplaceInvaliValues was On"
870 << std::endl;
871 statusAll = STATUS_FAILURE;
872 }
873 }
874 parser->GetScalarResult();
875 return statusAll;
876 }
877
TestErrors()878 bool TestErrors()
879 {
880 bool status = STATUS_SUCCESS;
881 std::cout << "Testing Errors"
882 << "...";
883
884 auto parser = vtkSmartPointer<vtkFunctionParser>::New();
885
886 auto errorObserver = vtkSmartPointer<vtkTest::ErrorObserver>::New();
887 parser->AddObserver(vtkCommand::ErrorEvent, errorObserver);
888
889 // Parse: no function has been set
890 parser->SetFunction("cos(a)");
891 parser->SetFunction(nullptr);
892 parser->IsScalarResult();
893 status &= !errorObserver->CheckErrorMessage("Parse: no function has been set");
894
895 double s = -2.0;
896 double v[3] = { 1.0, 2.0, 3.0 };
897 double w[3] = { 2.0, 1.0, 0.0 };
898 parser->SetScalarVariableValue("s", s);
899 parser->SetScalarVariableValue("zero", 0.0);
900 parser->SetVectorVariableValue("v", v[0], v[1], v[2]);
901 parser->SetVectorVariableValue("w", w[0], w[1], w[2]);
902
903 // addition expects either 2 vectors or 2 scalars
904 parser->SetFunction("s + v");
905 parser->IsScalarResult();
906 status &= !errorObserver->CheckErrorMessage("addition expects either 2 vectors or 2 scalars");
907
908 // subtraction expects either 2 vectors or 2 scalars
909 parser->SetFunction("s - v");
910 parser->IsScalarResult();
911 status &= !errorObserver->CheckErrorMessage("subtraction expects either 2 vectors or 2 scalars");
912
913 // multiply expecting either 2 scalars or a scalar and a vector
914 parser->SetFunction("v * w");
915 parser->IsScalarResult();
916 status &= !errorObserver->CheckErrorMessage(
917 "multiply expecting either 2 scalars or a scalar and a vector");
918
919 // can't divide vectors
920 parser->SetFunction("v / w");
921 parser->IsScalarResult();
922 status &= !errorObserver->CheckErrorMessage("can't divide vectors");
923
924 // can't raise a vector to a power
925 parser->SetFunction("v ^ 2");
926 parser->IsScalarResult();
927 status &= !errorObserver->CheckErrorMessage("can't raise a vector to a power");
928
929 // Vectors cannot be used in boolean expressions
930 parser->SetFunction("v | w");
931 parser->IsScalarResult();
932 status &= !errorObserver->CheckErrorMessage("Vectors cannot be used in boolean expressions");
933
934 // expecting a scalar, but got a vector
935 parser->SetFunction("cos(v)");
936 parser->IsScalarResult();
937 status &= !errorObserver->CheckErrorMessage("expecting a scalar, but got a vector");
938
939 // can't apply min to vectors
940 parser->SetFunction("min(v,w)");
941 parser->IsScalarResult();
942 status &= !errorObserver->CheckErrorMessage("can't apply min to vectors");
943 // can't apply max to vectors
944 parser->SetFunction("max(v,w)");
945 parser->IsScalarResult();
946 status &= !errorObserver->CheckErrorMessage("can't apply max to vectors");
947
948 // can't apply cross to scalars
949 parser->SetFunction("cross(s,w)");
950 parser->IsScalarResult();
951 status &= !errorObserver->CheckErrorMessage("can't apply cross to scalars");
952
953 // dot product does not operate on scalars
954 parser->SetFunction("s . v");
955 parser->IsScalarResult();
956 status &= !errorObserver->CheckErrorMessage("dot product does not operate on scalars");
957
958 // magnitude expects a vector, but got a scalar
959 parser->SetFunction("mag(s)");
960 parser->IsScalarResult();
961 status &= !errorObserver->CheckErrorMessage("magnitude expects a vector, but got a scalar");
962
963 // normalize expects a vector, but got a scalar
964 parser->SetFunction("norm(s)");
965 parser->IsScalarResult();
966 status &= !errorObserver->CheckErrorMessage("normalize expects a vector, but got a scalar");
967
968 // first argument of if(bool,valtrue,valfalse) cannot be a vector
969 parser->SetFunction("if(v,s,s)");
970 parser->IsScalarResult();
971 status &= !errorObserver->CheckErrorMessage(
972 "first argument of if(bool,valtrue,valfalse) cannot be a vector");
973
974 // first argument of if(bool,valtrue,valfalse) cannot be a vector
975 parser->SetFunction("if(v,s,s)");
976 parser->IsScalarResult();
977 status &= !errorObserver->CheckErrorMessage(
978 "first argument of if(bool,valtrue,valfalse) cannot be a vector");
979
980 // the if function expects the second and third arguments to be either 2 vectors or 2 scalars
981 parser->SetFunction("if(s,v,s)");
982 parser->IsScalarResult();
983 status &= !errorObserver->CheckErrorMessage(
984 "the if function expects the second and third arguments to be either 2 vectors or 2 scalars");
985
986 // Trying to take a natural logarithm of a non-positive value
987 parser->SetFunction("ln(s)");
988 parser->IsScalarResult();
989 status &=
990 !errorObserver->CheckErrorMessage("Trying to take a natural logarithm of a non-positive value");
991
992 // Trying to take a log10 of a non-positive value
993 parser->SetFunction("log10(s)");
994 parser->IsScalarResult();
995 status &= !errorObserver->CheckErrorMessage("Trying to take a log10 of a non-positive value");
996
997 // Trying to take a square root of a negative value
998 parser->SetFunction("sqrt(s)");
999 parser->IsScalarResult();
1000 status &= !errorObserver->CheckErrorMessage("Trying to take a square root of a negative value");
1001
1002 // Trying to take asin of a value < -1 or > 1
1003 parser->SetFunction("asin(s)");
1004 parser->IsScalarResult();
1005 status &= !errorObserver->CheckErrorMessage("Trying to take asin of a value < -1 or > 1");
1006
1007 // Trying to take acos of a value < -1 or > 1
1008 parser->SetFunction("acos(s)");
1009 parser->IsScalarResult();
1010 status &= !errorObserver->CheckErrorMessage("Trying to take acos of a value < -1 or > 1");
1011
1012 // Trying to divide by zero<
1013 parser->SetFunction("s/zero");
1014 parser->IsScalarResult();
1015 status &= !errorObserver->CheckErrorMessage("Trying to divide by zero");
1016
1017 // GetScalarResult: no valid scalar result
1018 parser->SetFunction("cross(v,w)");
1019 parser->GetScalarResult();
1020 status &= !errorObserver->CheckErrorMessage("GetScalarResult: no valid scalar result");
1021
1022 // GetVectorResult: no valid vector result
1023 parser->SetFunction("v . w");
1024 parser->GetVectorResult();
1025 status &= !errorObserver->CheckErrorMessage("GetVectorResult: no valid vector result");
1026
1027 // GetScalarVariableValue: scalar variable name ... does not exist
1028 parser->GetScalarVariableValue("xyz");
1029 status &= !errorObserver->CheckErrorMessage("GetScalarVariableValue: scalar variable name");
1030
1031 // GetScalarVariableValue: scalar variable number ... does not exist
1032 parser->GetScalarVariableValue(128);
1033 status &= !errorObserver->CheckErrorMessage("GetScalarVariableValue: scalar variable number");
1034
1035 // GetVectorVariableValue: vector variable name ... does not exist
1036 parser->GetVectorVariableValue("xyz");
1037 status &= !errorObserver->CheckErrorMessage("GetVectorVariableValue: vector variable name");
1038
1039 // GetVectorVariableValue: vector variable number ... does not exist
1040 parser->GetVectorVariableValue(128);
1041 status &= !errorObserver->CheckErrorMessage("GetVectorVariableValue: vector variable number");
1042
1043 // Syntax error: expecting a variable name
1044 parser->SetFunction("acos()");
1045 parser->IsScalarResult();
1046 status &= !errorObserver->CheckErrorMessage("Syntax error: expecting a variable name");
1047
1048 // Parse errors
1049 parser->SetFunction("-");
1050 parser->IsScalarResult();
1051 status &= !errorObserver->CheckErrorMessage("Syntax error: unary minus with no operand");
1052
1053 parser->SetFunction("s *");
1054 parser->IsScalarResult();
1055 status &= !errorObserver->CheckErrorMessage("Syntax error: expecting a variable name");
1056
1057 parser->SetFunction("cross(v)");
1058 parser->IsScalarResult();
1059 status &=
1060 !errorObserver->CheckErrorMessage("Syntax Error: two parameters separated by commas expected");
1061
1062 parser->SetFunction("if(v,s)");
1063 parser->IsScalarResult();
1064 status &= !errorObserver->CheckErrorMessage(
1065 "Syntax Error: three parameters separated by commas expected");
1066
1067 parser->SetFunction("s * (v + w");
1068 parser->IsScalarResult();
1069 status &= !errorObserver->CheckErrorMessage("Syntax Error: missing closing parenthesis");
1070
1071 parser->SetFunction("v + w)*s");
1072 parser->IsScalarResult();
1073 status &= !errorObserver->CheckErrorMessage("Syntax Error: mismatched parenthesis");
1074
1075 parser->SetFunction("s s");
1076 parser->IsScalarResult();
1077 status &= !errorObserver->CheckErrorMessage("Syntax error: operator expected");
1078
1079 parser->SetFunction("s*()");
1080 parser->IsScalarResult();
1081 status &= !errorObserver->CheckErrorMessage("Syntax error: expecting a variable name");
1082
1083 if (status == STATUS_SUCCESS)
1084 {
1085 std::cout << "PASSED\n";
1086 }
1087 else
1088 {
1089 std::cout << "FAILED\n";
1090 }
1091 return status;
1092 }
1093