mirror of
https://github.com/rizonesoft/Notepad3.git
synced 2026-06-17 21:03:19 +08:00
+ add: complete copy of TinyExpr fork for NP3 adaption
This commit is contained in:
parent
aa2883eeb3
commit
b9d1fc0d64
7
tinyexpr/.travis.yml
Normal file
7
tinyexpr/.travis.yml
Normal file
@ -0,0 +1,7 @@
|
||||
language: c
|
||||
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
|
||||
script: make
|
||||
10
tinyexpr/CONTRIBUTING
Normal file
10
tinyexpr/CONTRIBUTING
Normal file
@ -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.
|
||||
33
tinyexpr/Makefile
Normal file
33
tinyexpr/Makefile
Normal file
@ -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
|
||||
319
tinyexpr/README.md
Normal file
319
tinyexpr/README.md
Normal file
@ -0,0 +1,319 @@
|
||||
[](https://travis-ci.org/codeplea/tinyexpr)
|
||||
|
||||
|
||||
<img alt="TinyExpr logo" src="https://codeplea.com/public/content/tinyexpr_logo.png" align="right"/>
|
||||
|
||||
# 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 <stdio.h>
|
||||
|
||||
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:
|
||||
|
||||
<list> = <expr> {"," <expr>}
|
||||
<expr> = <term> {("+" | "-") <term>}
|
||||
<term> = <factor> {("*" | "/" | "%") <factor>}
|
||||
<factor> = <power> {"^" <power>}
|
||||
<power> = {("-" | "+")} <base>
|
||||
<base> = <constant>
|
||||
| <variable>
|
||||
| <function-0> {"(" ")"}
|
||||
| <function-1> <power>
|
||||
| <function-X> "(" <expr> {"," <expr>} ")"
|
||||
| "(" <list> ")"
|
||||
|
||||
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.
|
||||
|
||||
125
tinyexpr/benchmark.c
Normal file
125
tinyexpr/benchmark.c
Normal file
@ -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 <stdio.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
#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;
|
||||
}
|
||||
8
tinyexpr/doc/e1.dot
Normal file
8
tinyexpr/doc/e1.dot
Normal file
@ -0,0 +1,8 @@
|
||||
digraph G {
|
||||
"+" -> "sin";
|
||||
"+" -> div;
|
||||
"sin" -> "x";
|
||||
div -> "1";
|
||||
div -> "4";
|
||||
div [label="÷"]
|
||||
}
|
||||
BIN
tinyexpr/doc/e1.png
Normal file
BIN
tinyexpr/doc/e1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
5
tinyexpr/doc/e2.dot
Normal file
5
tinyexpr/doc/e2.dot
Normal file
@ -0,0 +1,5 @@
|
||||
digraph G {
|
||||
"+" -> "sin";
|
||||
"+" -> "0.25";
|
||||
"sin" -> "x";
|
||||
}
|
||||
BIN
tinyexpr/doc/e2.png
Normal file
BIN
tinyexpr/doc/e2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
10
tinyexpr/example.c
Normal file
10
tinyexpr/example.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include "tinyexpr.h"
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
38
tinyexpr/example2.c
Normal file
38
tinyexpr/example2.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include "tinyexpr.h"
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
35
tinyexpr/example3.c
Normal file
35
tinyexpr/example3.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include "tinyexpr.h"
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
/* 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;
|
||||
}
|
||||
128
tinyexpr/minctest.h
Normal file
128
tinyexpr/minctest.h
Normal file
@ -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 <stdio.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
/* 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__*/
|
||||
691
tinyexpr/test.c
Normal file
691
tinyexpr/test.c
Normal file
@ -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 <stdio.h>
|
||||
#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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user