diff --git a/tinyexpr/.travis.yml b/tinyexpr/.travis.yml
new file mode 100644
index 000000000..d275c1a7b
--- /dev/null
+++ b/tinyexpr/.travis.yml
@@ -0,0 +1,7 @@
+language: c
+
+compiler:
+ - clang
+ - gcc
+
+script: make
diff --git a/tinyexpr/CONTRIBUTING b/tinyexpr/CONTRIBUTING
new file mode 100644
index 000000000..f27fc96f8
--- /dev/null
+++ b/tinyexpr/CONTRIBUTING
@@ -0,0 +1,10 @@
+A core strength of TinyExpr is that it is small and simple. This makes it easy
+to add new features. However, if we keep adding new features, it'll no longer
+be small or simple. In other words, each new feature corrodes away at the core
+strength of TinyExpr.
+
+If you want to add a new feature, and you expect me to merge it, please discuss
+it with me before you go to that work. Open an issue at
+https://github.com/codeplea/tinyexpr and let us know what you're proposing.
+
+Bug fixes are always welcome and appreciated, of course.
diff --git a/tinyexpr/Makefile b/tinyexpr/Makefile
new file mode 100644
index 000000000..cc9765c88
--- /dev/null
+++ b/tinyexpr/Makefile
@@ -0,0 +1,33 @@
+CCFLAGS = -ansi -Wall -Wshadow -O2
+LFLAGS = -lm
+
+.PHONY = all clean
+
+all: test test_pr bench example example2 example3
+
+
+test: test.c tinyexpr.c
+ $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS)
+ ./$@
+
+test_pr: test.c tinyexpr.c
+ $(CC) $(CCFLAGS) -DTE_POW_FROM_RIGHT -DTE_NAT_LOG -o $@ $^ $(LFLAGS)
+ ./$@
+
+bench: benchmark.o tinyexpr.o
+ $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS)
+
+example: example.o tinyexpr.o
+ $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS)
+
+example2: example2.o tinyexpr.o
+ $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS)
+
+example3: example3.o tinyexpr.o
+ $(CC) $(CCFLAGS) -o $@ $^ $(LFLAGS)
+
+.c.o:
+ $(CC) -c $(CCFLAGS) $< -o $@
+
+clean:
+ rm -f *.o *.exe example example2 example3 bench test_pr test
diff --git a/tinyexpr/README.md b/tinyexpr/README.md
new file mode 100644
index 000000000..ad9f45a45
--- /dev/null
+++ b/tinyexpr/README.md
@@ -0,0 +1,319 @@
+[](https://travis-ci.org/codeplea/tinyexpr)
+
+
+
+
+# TinyExpr
+
+TinyExpr is a very small recursive descent parser and evaluation engine for
+math expressions. It's handy when you want to add the ability to evaluation
+math expressions at runtime without adding a bunch of cruft to you project.
+
+In addition to the standard math operators and precedence, TinyExpr also supports
+the standard C math functions and runtime binding of variables.
+
+## Features
+
+- **ANSI C with no dependencies**.
+- Single source file and header file.
+- Simple and fast.
+- Implements standard operators precedence.
+- Exposes standard C math functions (sin, sqrt, ln, etc.).
+- Can add custom functions and variables easily.
+- Can bind variables at eval-time.
+- Released under the zlib license - free for nearly any use.
+- Easy to use and integrate with your code
+- Thread-safe, provided that your *malloc* is.
+
+## Building
+
+TinyExpr is self-contained in two files: `tinyexpr.c` and `tinyexpr.h`. To use
+TinyExpr, simply add those two files to your project.
+
+## Short Example
+
+Here is a minimal example to evaluate an expression at runtime.
+
+```C
+ #include "tinyexpr.h"
+ printf("%f\n", te_interp("5*5", 0)); /* Prints 25. */
+```
+
+
+## Usage
+
+TinyExpr defines only four functions:
+
+```C
+ double te_interp(const char *expression, int *error);
+ te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error);
+ double te_eval(const te_expr *expr);
+ void te_free(te_expr *expr);
+```
+
+## te_interp
+```C
+ double te_interp(const char *expression, int *error);
+```
+
+`te_interp()` takes an expression and immediately returns the result of it. If there
+is a parse error, `te_interp()` returns NaN.
+
+If the `error` pointer argument is not 0, then `te_interp()` will set `*error` to the position
+of the parse error on failure, and set `*error` to 0 on success.
+
+**example usage:**
+
+```C
+ int error;
+
+ double a = te_interp("(5+5)", 0); /* Returns 10. */
+ double b = te_interp("(5+5)", &error); /* Returns 10, error is set to 0. */
+ double c = te_interp("(5+5", &error); /* Returns NaN, error is set to 4. */
+```
+
+## te_compile, te_eval, te_free
+```C
+ te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error);
+ double te_eval(const te_expr *n);
+ void te_free(te_expr *n);
+```
+
+Give `te_compile()` an expression with unbound variables and a list of
+variable names and pointers. `te_compile()` will return a `te_expr*` which can
+be evaluated later using `te_eval()`. On failure, `te_compile()` will return 0
+and optionally set the passed in `*error` to the location of the parse error.
+
+You may also compile expressions without variables by passing `te_compile()`'s second
+and thrid arguments as 0.
+
+Give `te_eval()` a `te_expr*` from `te_compile()`. `te_eval()` will evaluate the expression
+using the current variable values.
+
+After you're finished, make sure to call `te_free()`.
+
+**example usage:**
+
+```C
+ double x, y;
+ /* Store variable names and pointers. */
+ te_variable vars[] = {{"x", &x}, {"y", &y}};
+
+ int err;
+ /* Compile the expression with variables. */
+ te_expr *expr = te_compile("sqrt(x^2+y^2)", vars, 2, &err);
+
+ if (expr) {
+ x = 3; y = 4;
+ const double h1 = te_eval(expr); /* Returns 5. */
+
+ x = 5; y = 12;
+ const double h2 = te_eval(expr); /* Returns 13. */
+
+ te_free(expr);
+ } else {
+ printf("Parse error at %d\n", err);
+ }
+
+```
+
+## Longer Example
+
+Here is a complete example that will evaluate an expression passed in from the command
+line. It also does error checking and binds the variables `x` and `y` to *3* and *4*, respectively.
+
+```C
+ #include "tinyexpr.h"
+ #include
+
+ int main(int argc, char *argv[])
+ {
+ if (argc < 2) {
+ printf("Usage: example2 \"expression\"\n");
+ return 0;
+ }
+
+ const char *expression = argv[1];
+ printf("Evaluating:\n\t%s\n", expression);
+
+ /* This shows an example where the variables
+ * x and y are bound at eval-time. */
+ double x, y;
+ te_variable vars[] = {{"x", &x}, {"y", &y}};
+
+ /* This will compile the expression and check for errors. */
+ int err;
+ te_expr *n = te_compile(expression, vars, 2, &err);
+
+ if (n) {
+ /* The variables can be changed here, and eval can be called as many
+ * times as you like. This is fairly efficient because the parsing has
+ * already been done. */
+ x = 3; y = 4;
+ const double r = te_eval(n); printf("Result:\n\t%f\n", r);
+ te_free(n);
+ } else {
+ /* Show the user where the error is at. */
+ printf("\t%*s^\nError near here", err-1, "");
+ }
+
+ return 0;
+ }
+```
+
+
+This produces the output:
+
+ $ example2 "sqrt(x^2+y2)"
+ Evaluating:
+ sqrt(x^2+y2)
+ ^
+ Error near here
+
+
+ $ example2 "sqrt(x^2+y^2)"
+ Evaluating:
+ sqrt(x^2+y^2)
+ Result:
+ 5.000000
+
+
+## Binding to Custom Functions
+
+TinyExpr can also call to custom functions implemented in C. Here is a short example:
+
+```C
+double my_sum(double a, double b) {
+ /* Example C function that adds two numbers together. */
+ return a + b;
+}
+
+te_variable vars[] = {
+ {"mysum", my_sum, TE_FUNCTION2} /* TE_FUNCTION2 used because my_sum takes two arguments. */
+};
+
+te_expr *n = te_compile("mysum(5, 6)", vars, 1, 0);
+
+```
+
+
+## How it works
+
+`te_compile()` uses a simple recursive descent parser to compile your
+expression into a syntax tree. For example, the expression `"sin x + 1/4"`
+parses as:
+
+
+
+`te_compile()` also automatically prunes constant branches. In this example,
+the compiled expression returned by `te_compile()` would become:
+
+
+
+`te_eval()` will automatically load in any variables by their pointer, and then evaluate
+and return the result of the expression.
+
+`te_free()` should always be called when you're done with the compiled expression.
+
+
+## Speed
+
+
+TinyExpr is pretty fast compared to C when the expression is short, when the
+expression does hard calculations (e.g. exponentiation), and when some of the
+work can be simplified by `te_compile()`. TinyExpr is slow compared to C when the
+expression is long and involves only basic arithmetic.
+
+Here is some example performance numbers taken from the included
+**benchmark.c** program:
+
+| Expression | te_eval time | native C time | slowdown |
+| :------------- |-------------:| -----:|----:|
+| sqrt(a^1.5+a^2.5) | 15,641 ms | 14,478 ms | 8% slower |
+| a+5 | 765 ms | 563 ms | 36% slower |
+| a+(5*2) | 765 ms | 563 ms | 36% slower |
+| (a+5)*2 | 1422 ms | 563 ms | 153% slower |
+| (1/(a+1)+2/(a+2)+3/(a+3)) | 5,516 ms | 1,266 ms | 336% slower |
+
+
+
+## Grammar
+
+TinyExpr parses the following grammar:
+
+ = {"," }
+ = {("+" | "-") }
+ = {("*" | "/" | "%") }
+ = {"^" }
+ = {("-" | "+")}
+ =
+ |
+ | {"(" ")"}
+ |
+ | "(" {"," } ")"
+ | "(" ")"
+
+In addition, whitespace between tokens is ignored.
+
+Valid variable names consist of a lower case letter followed by any combination
+of: lower case letters *a* through *z*, the digits *0* through *9*, and
+underscore. Constants can be integers, decimal numbers, or in scientific
+notation (e.g. *1e3* for *1000*). A leading zero is not required (e.g. *.5*
+for *0.5*)
+
+
+## Functions supported
+
+TinyExpr supports addition (+), subtraction/negation (-), multiplication (\*),
+division (/), exponentiation (^) and modulus (%) with the normal operator
+precedence (the one exception being that exponentiation is evaluated
+left-to-right, but this can be changed - see below).
+
+The following C math functions are also supported:
+
+- abs (calls to *fabs*), acos, asin, atan, atan2, ceil, cos, cosh, exp, floor, ln (calls to *log*), log (calls to *log10* by default, see below), log10, pow, sin, sinh, sqrt, tan, tanh
+
+The following functions are also built-in and provided by TinyExpr:
+
+- fac (factorials e.g. `fac 5` == 120)
+- ncr (combinations e.g. `ncr(6,2)` == 15)
+- npr (permutations e.g. `npr(6,2)` == 30)
+
+Also, the following constants are available:
+
+- `pi`, `e`
+
+
+## Compile-time options
+
+
+By default, TinyExpr does exponentiation from left to right. For example:
+
+`a^b^c == (a^b)^c` and `-a^b == (-a)^b`
+
+This is by design. It's the way that spreadsheets do it (e.g. Excel, Google Sheets).
+
+
+If you would rather have exponentiation work from right to left, you need to
+define `TE_POW_FROM_RIGHT` when compiling `tinyexpr.c`. There is a
+commented-out define near the top of that file. With this option enabled, the
+behaviour is:
+
+`a^b^c == a^(b^c)` and `-a^b == -(a^b)`
+
+That will match how many scripting languages do it (e.g. Python, Ruby).
+
+Also, if you'd like `log` to default to the natural log instead of `log10`,
+then you can define `TE_NAT_LOG`.
+
+## Hints
+
+- All functions/types start with the letters *te*.
+
+- To allow constant optimization, surround constant expressions in parentheses.
+ For example "x+(1+5)" will evaluate the "(1+5)" expression at compile time and
+ compile the entire expression as "x+6", saving a runtime calculation. The
+ parentheses are important, because TinyExpr will not change the order of
+ evaluation. If you instead compiled "x+1+5" TinyExpr will insist that "1" is
+ added to "x" first, and "5" is added the result second.
+
diff --git a/tinyexpr/benchmark.c b/tinyexpr/benchmark.c
new file mode 100644
index 000000000..31d18faf8
--- /dev/null
+++ b/tinyexpr/benchmark.c
@@ -0,0 +1,125 @@
+/*
+ * TINYEXPR - Tiny recursive descent parser and evaluation engine in C
+ *
+ * Copyright (c) 2015, 2016 Lewis Van Winkle
+ *
+ * http://CodePlea.com
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgement in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include
+#include
+#include
+#include "tinyexpr.h"
+
+
+
+#define loops 10000
+
+
+
+typedef double (*function1)(double);
+
+void bench(const char *expr, function1 func) {
+ int i, j;
+ volatile double d;
+ double tmp;
+ clock_t start;
+
+ te_variable lk = {"a", &tmp};
+
+ printf("Expression: %s\n", expr);
+
+ printf("native ");
+ start = clock();
+ d = 0;
+ for (j = 0; j < loops; ++j)
+ for (i = 0; i < loops; ++i) {
+ tmp = i;
+ d += func(tmp);
+ }
+ const int nelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC;
+
+ /*Million floats per second input.*/
+ printf(" %.5g", d);
+ if (nelapsed)
+ printf("\t%5dms\t%5dmfps\n", nelapsed, loops * loops / nelapsed / 1000);
+ else
+ printf("\tinf\n");
+
+
+
+
+ printf("interp ");
+ te_expr *n = te_compile(expr, &lk, 1, 0);
+ start = clock();
+ d = 0;
+ for (j = 0; j < loops; ++j)
+ for (i = 0; i < loops; ++i) {
+ tmp = i;
+ d += te_eval(n);
+ }
+ const int eelapsed = (clock() - start) * 1000 / CLOCKS_PER_SEC;
+ te_free(n);
+
+ /*Million floats per second input.*/
+ printf(" %.5g", d);
+ if (eelapsed)
+ printf("\t%5dms\t%5dmfps\n", eelapsed, loops * loops / eelapsed / 1000);
+ else
+ printf("\tinf\n");
+
+
+ printf("%.2f%% longer\n", (((double)eelapsed / nelapsed) - 1.0) * 100.0);
+
+
+ printf("\n");
+}
+
+
+double a5(double a) {
+ return a+5;
+}
+
+double a52(double a) {
+ return (a+5)*2;
+}
+
+double a10(double a) {
+ return a+(5*2);
+}
+
+double as(double a) {
+ return sqrt(pow(a, 1.5) + pow(a, 2.5));
+}
+
+double al(double a) {
+ return (1/(a+1)+2/(a+2)+3/(a+3));
+}
+
+int main(int argc, char *argv[])
+{
+
+ bench("sqrt(a^1.5+a^2.5)", as);
+ bench("a+5", a5);
+ bench("a+(5*2)", a10);
+ bench("(a+5)*2", a52);
+ bench("(1/(a+1)+2/(a+2)+3/(a+3))", al);
+
+ return 0;
+}
diff --git a/tinyexpr/doc/e1.dot b/tinyexpr/doc/e1.dot
new file mode 100644
index 000000000..6a631aba6
--- /dev/null
+++ b/tinyexpr/doc/e1.dot
@@ -0,0 +1,8 @@
+digraph G {
+ "+" -> "sin";
+ "+" -> div;
+ "sin" -> "x";
+ div -> "1";
+ div -> "4";
+ div [label="÷"]
+}
diff --git a/tinyexpr/doc/e1.png b/tinyexpr/doc/e1.png
new file mode 100644
index 000000000..7f7a4500b
Binary files /dev/null and b/tinyexpr/doc/e1.png differ
diff --git a/tinyexpr/doc/e2.dot b/tinyexpr/doc/e2.dot
new file mode 100644
index 000000000..d6067228b
--- /dev/null
+++ b/tinyexpr/doc/e2.dot
@@ -0,0 +1,5 @@
+digraph G {
+ "+" -> "sin";
+ "+" -> "0.25";
+ "sin" -> "x";
+}
diff --git a/tinyexpr/doc/e2.png b/tinyexpr/doc/e2.png
new file mode 100644
index 000000000..cebb1dbc4
Binary files /dev/null and b/tinyexpr/doc/e2.png differ
diff --git a/tinyexpr/example.c b/tinyexpr/example.c
new file mode 100644
index 000000000..040093fd2
--- /dev/null
+++ b/tinyexpr/example.c
@@ -0,0 +1,10 @@
+#include "tinyexpr.h"
+#include
+
+int main(int argc, char *argv[])
+{
+ const char *c = "sqrt(5^2+7^2+11^2+(8-2)^2)";
+ double r = te_interp(c, 0);
+ printf("The expression:\n\t%s\nevaluates to:\n\t%f\n", c, r);
+ return 0;
+}
diff --git a/tinyexpr/example2.c b/tinyexpr/example2.c
new file mode 100644
index 000000000..70d05d190
--- /dev/null
+++ b/tinyexpr/example2.c
@@ -0,0 +1,38 @@
+#include "tinyexpr.h"
+#include
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ printf("Usage: example2 \"expression\"\n");
+ return 0;
+ }
+
+ const char *expression = argv[1];
+ printf("Evaluating:\n\t%s\n", expression);
+
+ /* This shows an example where the variables
+ * x and y are bound at eval-time. */
+ double x, y;
+ te_variable vars[] = {{"x", &x}, {"y", &y}};
+
+ /* This will compile the expression and check for errors. */
+ int err;
+ te_expr *n = te_compile(expression, vars, 2, &err);
+
+ if (n) {
+ /* The variables can be changed here, and eval can be called as many
+ * times as you like. This is fairly efficient because the parsing has
+ * already been done. */
+ x = 3; y = 4;
+ const double r = te_eval(n); printf("Result:\n\t%f\n", r);
+
+ te_free(n);
+ } else {
+ /* Show the user where the error is at. */
+ printf("\t%*s^\nError near here", err-1, "");
+ }
+
+
+ return 0;
+}
diff --git a/tinyexpr/example3.c b/tinyexpr/example3.c
new file mode 100644
index 000000000..80fe9da44
--- /dev/null
+++ b/tinyexpr/example3.c
@@ -0,0 +1,35 @@
+#include "tinyexpr.h"
+#include
+
+
+/* An example of calling a C function. */
+double my_sum(double a, double b) {
+ printf("Called C function with %f and %f.\n", a, b);
+ return a + b;
+}
+
+
+int main(int argc, char *argv[])
+{
+ te_variable vars[] = {
+ {"mysum", my_sum, TE_FUNCTION2}
+ };
+
+ const char *expression = "mysum(5, 6)";
+ printf("Evaluating:\n\t%s\n", expression);
+
+ int err;
+ te_expr *n = te_compile(expression, vars, 1, &err);
+
+ if (n) {
+ const double r = te_eval(n);
+ printf("Result:\n\t%f\n", r);
+ te_free(n);
+ } else {
+ /* Show the user where the error is at. */
+ printf("\t%*s^\nError near here", err-1, "");
+ }
+
+
+ return 0;
+}
diff --git a/tinyexpr/minctest.h b/tinyexpr/minctest.h
new file mode 100644
index 000000000..6180de840
--- /dev/null
+++ b/tinyexpr/minctest.h
@@ -0,0 +1,128 @@
+/*
+ *
+ * MINCTEST - Minimal C Test Library - 0.1
+ *
+ * Copyright (c) 2014, 2015 Lewis Van Winkle
+ *
+ * http://CodePlea.com
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgement in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ */
+
+
+
+/*
+ * MINCTEST - Minimal testing library for C
+ *
+ *
+ * Example:
+ *
+ * void test1() {
+ * lok('a' == 'a');
+ * }
+ *
+ * void test2() {
+ * lequal(5, 6);
+ * lfequal(5.5, 5.6);
+ * }
+ *
+ * int main() {
+ * lrun("test1", test1);
+ * lrun("test2", test2);
+ * lresults();
+ * return lfails != 0;
+ * }
+ *
+ *
+ *
+ * Hints:
+ * All functions/variables start with the letter 'l'.
+ *
+ */
+
+
+#ifndef __MINCTEST_H__
+#define __MINCTEST_H__
+
+#include
+#include
+#include
+
+
+/* How far apart can floats be before we consider them unequal. */
+#define LTEST_FLOAT_TOLERANCE 0.001
+
+
+/* Track the number of passes, fails. */
+/* NB this is made for all tests to be in one file. */
+static int ltests = 0;
+static int lfails = 0;
+
+
+/* Display the test results. */
+#define lresults() do {\
+ if (lfails == 0) {\
+ printf("ALL TESTS PASSED (%d/%d)\n", ltests, ltests);\
+ } else {\
+ printf("SOME TESTS FAILED (%d/%d)\n", ltests-lfails, ltests);\
+ }\
+} while (0)
+
+
+/* Run a test. Name can be any string to print out, test is the function name to call. */
+#define lrun(name, test) do {\
+ const int ts = ltests;\
+ const int fs = lfails;\
+ const clock_t start = clock();\
+ printf("\t%-14s", name);\
+ test();\
+ printf("pass:%2d fail:%2d %4dms\n",\
+ (ltests-ts)-(lfails-fs), lfails-fs,\
+ (int)((clock() - start) * 1000 / CLOCKS_PER_SEC));\
+} while (0)
+
+
+/* Assert a true statement. */
+#define lok(test) do {\
+ ++ltests;\
+ if (!(test)) {\
+ ++lfails;\
+ printf("%s:%d error \n", __FILE__, __LINE__);\
+ }} while (0)
+
+
+/* Assert two integers are equal. */
+#define lequal(a, b) do {\
+ ++ltests;\
+ if ((a) != (b)) {\
+ ++lfails;\
+ printf("%s:%d (%d != %d)\n", __FILE__, __LINE__, (a), (b));\
+ }} while (0)
+
+
+/* Assert two floats are equal (Within LTEST_FLOAT_TOLERANCE). */
+#define lfequal(a, b) do {\
+ ++ltests;\
+ const double __LF_COMPARE = fabs((double)(a)-(double)(b));\
+ if (__LF_COMPARE > LTEST_FLOAT_TOLERANCE || (__LF_COMPARE != __LF_COMPARE)) {\
+ ++lfails;\
+ printf("%s:%d (%f != %f)\n", __FILE__, __LINE__, (double)(a), (double)(b));\
+ }} while (0)
+
+
+#endif /*__MINCTEST_H__*/
diff --git a/tinyexpr/test.c b/tinyexpr/test.c
new file mode 100644
index 000000000..c772950a8
--- /dev/null
+++ b/tinyexpr/test.c
@@ -0,0 +1,691 @@
+/*
+ * TINYEXPR - Tiny recursive descent parser and evaluation engine in C
+ *
+ * Copyright (c) 2015, 2016 Lewis Van Winkle
+ *
+ * http://CodePlea.com
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgement in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+
+#include "tinyexpr.h"
+#include
+#include "minctest.h"
+
+
+typedef struct {
+ const char *expr;
+ double answer;
+} test_case;
+
+typedef struct {
+ const char *expr1;
+ const char *expr2;
+} test_equ;
+
+
+
+void test_results() {
+ test_case cases[] = {
+ {"1", 1},
+ {"1 ", 1},
+ {"(1)", 1},
+
+ {"pi", 3.14159},
+ {"atan(1)*4 - pi", 0},
+ {"e", 2.71828},
+
+ {"2+1", 2+1},
+ {"(((2+(1))))", 2+1},
+ {"3+2", 3+2},
+
+ {"3+2+4", 3+2+4},
+ {"(3+2)+4", 3+2+4},
+ {"3+(2+4)", 3+2+4},
+ {"(3+2+4)", 3+2+4},
+
+ {"3*2*4", 3*2*4},
+ {"(3*2)*4", 3*2*4},
+ {"3*(2*4)", 3*2*4},
+ {"(3*2*4)", 3*2*4},
+
+ {"3-2-4", 3-2-4},
+ {"(3-2)-4", (3-2)-4},
+ {"3-(2-4)", 3-(2-4)},
+ {"(3-2-4)", 3-2-4},
+
+ {"3/2/4", 3.0/2.0/4.0},
+ {"(3/2)/4", (3.0/2.0)/4.0},
+ {"3/(2/4)", 3.0/(2.0/4.0)},
+ {"(3/2/4)", 3.0/2.0/4.0},
+
+ {"(3*2/4)", 3.0*2.0/4.0},
+ {"(3/2*4)", 3.0/2.0*4.0},
+ {"3*(2/4)", 3.0*(2.0/4.0)},
+
+ {"asin sin .5", 0.5},
+ {"sin asin .5", 0.5},
+ {"ln exp .5", 0.5},
+ {"exp ln .5", 0.5},
+
+ {"asin sin-.5", -0.5},
+ {"asin sin-0.5", -0.5},
+ {"asin sin -0.5", -0.5},
+ {"asin (sin -0.5)", -0.5},
+ {"asin (sin (-0.5))", -0.5},
+ {"asin sin (-0.5)", -0.5},
+ {"(asin sin (-0.5))", -0.5},
+
+ {"log10 1000", 3},
+ {"log10 1e3", 3},
+ {"log10 1000", 3},
+ {"log10 1e3", 3},
+ {"log10(1000)", 3},
+ {"log10(1e3)", 3},
+ {"log10 1.0e3", 3},
+ {"10^5*5e-5", 5},
+
+#ifdef TE_NAT_LOG
+ {"log 1000", 6.9078},
+ {"log e", 1},
+ {"log (e^10)", 10},
+#else
+ {"log 1000", 3},
+#endif
+
+ {"ln (e^10)", 10},
+ {"100^.5+1", 11},
+ {"100 ^.5+1", 11},
+ {"100^+.5+1", 11},
+ {"100^--.5+1", 11},
+ {"100^---+-++---++-+-+-.5+1", 11},
+
+ {"100^-.5+1", 1.1},
+ {"100^---.5+1", 1.1},
+ {"100^+---.5+1", 1.1},
+ {"1e2^+---.5e0+1e0", 1.1},
+ {"--(1e2^(+(-(-(-.5e0))))+1e0)", 1.1},
+
+ {"sqrt 100 + 7", 17},
+ {"sqrt 100 * 7", 70},
+ {"sqrt (100 * 100)", 100},
+
+ {"1,2", 2},
+ {"1,2+1", 3},
+ {"1+1,2+2,2+1", 3},
+ {"1,2,3", 3},
+ {"(1,2),3", 3},
+ {"1,(2,3)", 3},
+ {"-(1,(2,3))", -3},
+
+ {"2^2", 4},
+ {"pow(2,2)", 4},
+
+ {"atan2(1,1)", 0.7854},
+ {"atan2(1,2)", 0.4636},
+ {"atan2(2,1)", 1.1071},
+ {"atan2(3,4)", 0.6435},
+ {"atan2(3+3,4*2)", 0.6435},
+ {"atan2(3+3,(4*2))", 0.6435},
+ {"atan2((3+3),4*2)", 0.6435},
+ {"atan2((3+3),(4*2))", 0.6435},
+
+ };
+
+
+ int i;
+ for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
+ const char *expr = cases[i].expr;
+ const double answer = cases[i].answer;
+
+ int err;
+ const double ev = te_interp(expr, &err);
+ lok(!err);
+ lfequal(ev, answer);
+
+ if (err) {
+ printf("FAILED: %s (%d)\n", expr, err);
+ }
+ }
+}
+
+
+void test_syntax() {
+ test_case errors[] = {
+ {"", 1},
+ {"1+", 2},
+ {"1)", 2},
+ {"(1", 2},
+ {"1**1", 3},
+ {"1*2(+4", 4},
+ {"1*2(1+4", 4},
+ {"a+5", 1},
+ {"A+5", 1},
+ {"Aa+5", 1},
+ {"1^^5", 3},
+ {"1**5", 3},
+ {"sin(cos5", 8},
+ };
+
+
+ int i;
+ for (i = 0; i < sizeof(errors) / sizeof(test_case); ++i) {
+ const char *expr = errors[i].expr;
+ const int e = errors[i].answer;
+
+ int err;
+ const double r = te_interp(expr, &err);
+ lequal(err, e);
+ lok(r != r);
+
+ te_expr *n = te_compile(expr, 0, 0, &err);
+ lequal(err, e);
+ lok(!n);
+
+ if (err != e) {
+ printf("FAILED: %s\n", expr);
+ }
+
+ const double k = te_interp(expr, 0);
+ lok(k != k);
+ }
+}
+
+
+void test_nans() {
+
+ const char *nans[] = {
+ "0/0",
+ "1%0",
+ "1%(1%0)",
+ "(1%0)%1",
+ "fac(-1)",
+ "ncr(2, 4)",
+ "ncr(-2, 4)",
+ "ncr(2, -4)",
+ "npr(2, 4)",
+ "npr(-2, 4)",
+ "npr(2, -4)",
+ };
+
+ int i;
+ for (i = 0; i < sizeof(nans) / sizeof(const char *); ++i) {
+ const char *expr = nans[i];
+
+ int err;
+ const double r = te_interp(expr, &err);
+ lequal(err, 0);
+ lok(r != r);
+
+ te_expr *n = te_compile(expr, 0, 0, &err);
+ lok(n);
+ lequal(err, 0);
+ const double c = te_eval(n);
+ lok(c != c);
+ te_free(n);
+ }
+}
+
+
+void test_infs() {
+
+ const char *infs[] = {
+ "1/0",
+ "log(0)",
+ "pow(2,10000000)",
+ "fac(300)",
+ "ncr(300,100)",
+ "ncr(300000,100)",
+ "ncr(300000,100)*8",
+ "npr(3,2)*ncr(300000,100)",
+ "npr(100,90)",
+ "npr(30,25)",
+ };
+
+ int i;
+ for (i = 0; i < sizeof(infs) / sizeof(const char *); ++i) {
+ const char *expr = infs[i];
+
+ int err;
+ const double r = te_interp(expr, &err);
+ lequal(err, 0);
+ lok(r == r + 1);
+
+ te_expr *n = te_compile(expr, 0, 0, &err);
+ lok(n);
+ lequal(err, 0);
+ const double c = te_eval(n);
+ lok(c == c + 1);
+ te_free(n);
+ }
+}
+
+
+void test_variables() {
+
+ double x, y, test;
+ te_variable lookup[] = {{"x", &x}, {"y", &y}, {"te_st", &test}};
+
+ int err;
+
+ te_expr *expr1 = te_compile("cos x + sin y", lookup, 2, &err);
+ lok(expr1);
+ lok(!err);
+
+ te_expr *expr2 = te_compile("x+x+x-y", lookup, 2, &err);
+ lok(expr2);
+ lok(!err);
+
+ te_expr *expr3 = te_compile("x*y^3", lookup, 2, &err);
+ lok(expr3);
+ lok(!err);
+
+ te_expr *expr4 = te_compile("te_st+5", lookup, 3, &err);
+ lok(expr4);
+ lok(!err);
+
+ for (y = 2; y < 3; ++y) {
+ for (x = 0; x < 5; ++x) {
+ double ev;
+
+ ev = te_eval(expr1);
+ lfequal(ev, cos(x) + sin(y));
+
+ ev = te_eval(expr2);
+ lfequal(ev, x+x+x-y);
+
+ ev = te_eval(expr3);
+ lfequal(ev, x*y*y*y);
+
+ test = x;
+ ev = te_eval(expr4);
+ lfequal(ev, x+5);
+ }
+ }
+
+ te_free(expr1);
+ te_free(expr2);
+ te_free(expr3);
+ te_free(expr4);
+
+
+
+ te_expr *expr5 = te_compile("xx*y^3", lookup, 2, &err);
+ lok(!expr5);
+ lok(err);
+
+ te_expr *expr6 = te_compile("tes", lookup, 3, &err);
+ lok(!expr6);
+ lok(err);
+
+ te_expr *expr7 = te_compile("sinn x", lookup, 2, &err);
+ lok(!expr7);
+ lok(err);
+
+ te_expr *expr8 = te_compile("si x", lookup, 2, &err);
+ lok(!expr8);
+ lok(err);
+}
+
+
+
+#define cross_check(a, b) do {\
+ if ((b)!=(b)) break;\
+ expr = te_compile((a), lookup, 2, &err);\
+ lfequal(te_eval(expr), (b));\
+ lok(!err);\
+ te_free(expr);\
+}while(0)
+
+void test_functions() {
+
+ double x, y;
+ te_variable lookup[] = {{"x", &x}, {"y", &y}};
+
+ int err;
+ te_expr *expr;
+
+ for (x = -5; x < 5; x += .2) {
+ cross_check("abs x", fabs(x));
+ cross_check("acos x", acos(x));
+ cross_check("asin x", asin(x));
+ cross_check("atan x", atan(x));
+ cross_check("ceil x", ceil(x));
+ cross_check("cos x", cos(x));
+ cross_check("cosh x", cosh(x));
+ cross_check("exp x", exp(x));
+ cross_check("floor x", floor(x));
+ cross_check("ln x", log(x));
+ cross_check("log10 x", log10(x));
+ cross_check("sin x", sin(x));
+ cross_check("sinh x", sinh(x));
+ cross_check("sqrt x", sqrt(x));
+ cross_check("tan x", tan(x));
+ cross_check("tanh x", tanh(x));
+
+ for (y = -2; y < 2; y += .2) {
+ if (fabs(x) < 0.01) break;
+ cross_check("atan2(x,y)", atan2(x, y));
+ cross_check("pow(x,y)", pow(x, y));
+ }
+ }
+}
+
+
+double sum0() {
+ return 6;
+}
+double sum1(double a) {
+ return a * 2;
+}
+double sum2(double a, double b) {
+ return a + b;
+}
+double sum3(double a, double b, double c) {
+ return a + b + c;
+}
+double sum4(double a, double b, double c, double d) {
+ return a + b + c + d;
+}
+double sum5(double a, double b, double c, double d, double e) {
+ return a + b + c + d + e;
+}
+double sum6(double a, double b, double c, double d, double e, double f) {
+ return a + b + c + d + e + f;
+}
+double sum7(double a, double b, double c, double d, double e, double f, double g) {
+ return a + b + c + d + e + f + g;
+}
+
+
+void test_dynamic() {
+
+ double x, f;
+ te_variable lookup[] = {
+ {"x", &x},
+ {"f", &f},
+ {"sum0", sum0, TE_FUNCTION0},
+ {"sum1", sum1, TE_FUNCTION1},
+ {"sum2", sum2, TE_FUNCTION2},
+ {"sum3", sum3, TE_FUNCTION3},
+ {"sum4", sum4, TE_FUNCTION4},
+ {"sum5", sum5, TE_FUNCTION5},
+ {"sum6", sum6, TE_FUNCTION6},
+ {"sum7", sum7, TE_FUNCTION7},
+ };
+
+ test_case cases[] = {
+ {"x", 2},
+ {"f+x", 7},
+ {"x+x", 4},
+ {"x+f", 7},
+ {"f+f", 10},
+ {"f+sum0", 11},
+ {"sum0+sum0", 12},
+ {"sum0()+sum0", 12},
+ {"sum0+sum0()", 12},
+ {"sum0()+(0)+sum0()", 12},
+ {"sum1 sum0", 12},
+ {"sum1(sum0)", 12},
+ {"sum1 f", 10},
+ {"sum1 x", 4},
+ {"sum2 (sum0, x)", 8},
+ {"sum3 (sum0, x, 2)", 10},
+ {"sum2(2,3)", 5},
+ {"sum3(2,3,4)", 9},
+ {"sum4(2,3,4,5)", 14},
+ {"sum5(2,3,4,5,6)", 20},
+ {"sum6(2,3,4,5,6,7)", 27},
+ {"sum7(2,3,4,5,6,7,8)", 35},
+ };
+
+ x = 2;
+ f = 5;
+
+ int i;
+ for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
+ const char *expr = cases[i].expr;
+ const double answer = cases[i].answer;
+
+ int err;
+ te_expr *ex = te_compile(expr, lookup, sizeof(lookup)/sizeof(te_variable), &err);
+ lok(ex);
+ lfequal(te_eval(ex), answer);
+ te_free(ex);
+ }
+}
+
+
+double clo0(void *context) {
+ if (context) return *((double*)context) + 6;
+ return 6;
+}
+double clo1(void *context, double a) {
+ if (context) return *((double*)context) + a * 2;
+ return a * 2;
+}
+double clo2(void *context, double a, double b) {
+ if (context) return *((double*)context) + a + b;
+ return a + b;
+}
+
+double cell(void *context, double a) {
+ double *c = context;
+ return c[(int)a];
+}
+
+void test_closure() {
+
+ double extra;
+ double c[] = {5,6,7,8,9};
+
+ te_variable lookup[] = {
+ {"c0", clo0, TE_CLOSURE0, &extra},
+ {"c1", clo1, TE_CLOSURE1, &extra},
+ {"c2", clo2, TE_CLOSURE2, &extra},
+ {"cell", cell, TE_CLOSURE1, c},
+ };
+
+ test_case cases[] = {
+ {"c0", 6},
+ {"c1 4", 8},
+ {"c2 (10, 20)", 30},
+ };
+
+ int i;
+ for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
+ const char *expr = cases[i].expr;
+ const double answer = cases[i].answer;
+
+ int err;
+ te_expr *ex = te_compile(expr, lookup, sizeof(lookup)/sizeof(te_variable), &err);
+ lok(ex);
+
+ extra = 0;
+ lfequal(te_eval(ex), answer + extra);
+
+ extra = 10;
+ lfequal(te_eval(ex), answer + extra);
+
+ te_free(ex);
+ }
+
+
+ test_case cases2[] = {
+ {"cell 0", 5},
+ {"cell 1", 6},
+ {"cell 0 + cell 1", 11},
+ {"cell 1 * cell 3 + cell 4", 57},
+ };
+
+ for (i = 0; i < sizeof(cases2) / sizeof(test_case); ++i) {
+ const char *expr = cases2[i].expr;
+ const double answer = cases2[i].answer;
+
+ int err;
+ te_expr *ex = te_compile(expr, lookup, sizeof(lookup)/sizeof(te_variable), &err);
+ lok(ex);
+ lfequal(te_eval(ex), answer);
+ te_free(ex);
+ }
+}
+
+void test_optimize() {
+
+ test_case cases[] = {
+ {"5+5", 10},
+ {"pow(2,2)", 4},
+ {"sqrt 100", 10},
+ {"pi * 2", 6.2832},
+ };
+
+ int i;
+ for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
+ const char *expr = cases[i].expr;
+ const double answer = cases[i].answer;
+
+ int err;
+ te_expr *ex = te_compile(expr, 0, 0, &err);
+ lok(ex);
+
+ /* The answer should be know without
+ * even running eval. */
+ lfequal(ex->value, answer);
+ lfequal(te_eval(ex), answer);
+
+ te_free(ex);
+ }
+}
+
+void test_pow() {
+#ifdef TE_POW_FROM_RIGHT
+ test_equ cases[] = {
+ {"2^3^4", "2^(3^4)"},
+ {"-2^2", "-(2^2)"},
+ {"--2^2", "(2^2)"},
+ {"---2^2", "-(2^2)"},
+ {"-(2)^2", "-(2^2)"},
+ {"-(2*1)^2", "-(2^2)"},
+ {"-2^2", "-4"},
+ {"2^1.1^1.2^1.3", "2^(1.1^(1.2^1.3))"},
+ {"-a^b", "-(a^b)"},
+ {"-a^-b", "-(a^-b)"}
+ };
+#else
+ test_equ cases[] = {
+ {"2^3^4", "(2^3)^4"},
+ {"-2^2", "(-2)^2"},
+ {"--2^2", "2^2"},
+ {"---2^2", "(-2)^2"},
+ {"-2^2", "4"},
+ {"2^1.1^1.2^1.3", "((2^1.1)^1.2)^1.3"},
+ {"-a^b", "(-a)^b"},
+ {"-a^-b", "(-a)^(-b)"}
+ };
+#endif
+
+ double a = 2, b = 3;
+
+ te_variable lookup[] = {
+ {"a", &a},
+ {"b", &b}
+ };
+
+ int i;
+ for (i = 0; i < sizeof(cases) / sizeof(test_equ); ++i) {
+ const char *expr1 = cases[i].expr1;
+ const char *expr2 = cases[i].expr2;
+
+ te_expr *ex1 = te_compile(expr1, lookup, sizeof(lookup)/sizeof(te_variable), 0);
+ te_expr *ex2 = te_compile(expr2, lookup, sizeof(lookup)/sizeof(te_variable), 0);
+
+ lok(ex1);
+ lok(ex2);
+
+ double r1 = te_eval(ex1);
+ double r2 = te_eval(ex2);
+
+ fflush(stdout);
+ lfequal(r1, r2);
+
+ te_free(ex1);
+ te_free(ex2);
+ }
+
+}
+
+void test_combinatorics() {
+ test_case cases[] = {
+ {"fac(0)", 1},
+ {"fac(0.2)", 1},
+ {"fac(1)", 1},
+ {"fac(2)", 2},
+ {"fac(3)", 6},
+ {"fac(4.8)", 24},
+ {"fac(10)", 3628800},
+
+ {"ncr(0,0)", 1},
+ {"ncr(10,1)", 10},
+ {"ncr(10,0)", 1},
+ {"ncr(10,10)", 1},
+ {"ncr(16,7)", 11440},
+ {"ncr(16,9)", 11440},
+ {"ncr(100,95)", 75287520},
+
+ {"npr(0,0)", 1},
+ {"npr(10,1)", 10},
+ {"npr(10,0)", 1},
+ {"npr(10,10)", 3628800},
+ {"npr(20,5)", 1860480},
+ {"npr(100,4)", 94109400},
+ };
+
+
+ int i;
+ for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
+ const char *expr = cases[i].expr;
+ const double answer = cases[i].answer;
+
+ int err;
+ const double ev = te_interp(expr, &err);
+ lok(!err);
+ lfequal(ev, answer);
+
+ if (err) {
+ printf("FAILED: %s (%d)\n", expr, err);
+ }
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ lrun("Results", test_results);
+ lrun("Syntax", test_syntax);
+ lrun("NaNs", test_nans);
+ lrun("INFs", test_infs);
+ lrun("Variables", test_variables);
+ lrun("Functions", test_functions);
+ lrun("Dynamic", test_dynamic);
+ lrun("Closure", test_closure);
+ lrun("Optimize", test_optimize);
+ lrun("Pow", test_pow);
+ lrun("Combinatorics", test_combinatorics);
+ lresults();
+
+ return lfails != 0;
+}