Skip to content

Commit bc132d5

Browse files
committed
2 parents ce9d1e5 + b585591 commit bc132d5

File tree

3 files changed

+149
-18
lines changed

3 files changed

+149
-18
lines changed

README.md

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<img alt="TinyExpr logo" src="https://codeplea.com/public/content/tinyexpr_logo.png" align="right"/>
55

6-
#TinyExpr
6+
# TinyExpr
77

88
TinyExpr is a very small recursive descent parser and evaluation engine for
99
math expressions. It's handy when you want to add the ability to evaluation
@@ -12,7 +12,7 @@ math expressions at runtime without adding a bunch of cruft to you project.
1212
In addition to the standard math operators and precedence, TinyExpr also supports
1313
the standard C math functions and runtime binding of variables.
1414

15-
##Features
15+
## Features
1616

1717
- **ANSI C with no dependencies**.
1818
- Single source file and header file.
@@ -25,12 +25,12 @@ the standard C math functions and runtime binding of variables.
2525
- Easy to use and integrate with your code
2626
- Thread-safe, provided that your *malloc* is.
2727

28-
##Building
28+
## Building
2929

3030
TinyExpr is self-contained in two files: `tinyexpr.c` and `tinyexpr.h`. To use
3131
TinyExpr, simply add those two files to your project.
3232

33-
##Short Example
33+
## Short Example
3434

3535
Here is a minimal example to evaluate an expression at runtime.
3636

@@ -40,7 +40,7 @@ Here is a minimal example to evaluate an expression at runtime.
4040
```
4141
4242
43-
##Usage
43+
## Usage
4444
4545
TinyExpr defines only four functions:
4646
@@ -51,7 +51,7 @@ TinyExpr defines only four functions:
5151
void te_free(te_expr *expr);
5252
```
5353

54-
##te_interp
54+
## te_interp
5555
```C
5656
double te_interp(const char *expression, int *error);
5757
```
@@ -72,7 +72,7 @@ of the parse error on failure, and set `*error` to 0 on success.
7272
double c = te_interp("(5+5", &error); /* Returns NaN, error is set to 4. */
7373
```
7474

75-
##te_compile, te_eval, te_free
75+
## te_compile, te_eval, te_free
7676
```C
7777
te_expr *te_compile(const char *expression, const te_variable *lookup, int lookup_len, int *error);
7878
double te_eval(const te_expr *n);
@@ -117,7 +117,7 @@ After you're finished, make sure to call `te_free()`.
117117
118118
```
119119

120-
##Longer Example
120+
## Longer Example
121121

122122
Here is a complete example that will evaluate an expression passed in from the command
123123
line. It also does error checking and binds the variables `x` and `y` to *3* and *4*, respectively.
@@ -178,7 +178,7 @@ This produces the output:
178178
5.000000
179179
180180
181-
##Binding to Custom Functions
181+
## Binding to Custom Functions
182182
183183
TinyExpr can also call to custom functions implemented in C. Here is a short example:
184184
@@ -197,7 +197,7 @@ te_expr *n = te_compile("mysum(5, 6)", vars, 1, 0);
197197
```
198198

199199

200-
##How it works
200+
## How it works
201201

202202
`te_compile()` uses a simple recursive descent parser to compile your
203203
expression into a syntax tree. For example, the expression `"sin x + 1/4"`
@@ -216,7 +216,7 @@ and return the result of the expression.
216216
`te_free()` should always be called when you're done with the compiled expression.
217217

218218

219-
##Speed
219+
## Speed
220220

221221

222222
TinyExpr is pretty fast compared to C when the expression is short, when the
@@ -237,7 +237,7 @@ Here is some example performance numbers taken from the included
237237

238238

239239

240-
##Grammar
240+
## Grammar
241241

242242
TinyExpr parses the following grammar:
243243

@@ -262,33 +262,39 @@ notation (e.g. *1e3* for *1000*). A leading zero is not required (e.g. *.5*
262262
for *0.5*)
263263

264264

265-
##Functions supported
265+
## Functions supported
266266

267267
TinyExpr supports addition (+), subtraction/negation (-), multiplication (\*),
268268
division (/), exponentiation (^) and modulus (%) with the normal operator
269269
precedence (the one exception being that exponentiation is evaluated
270270
left-to-right, but this can be changed - see below).
271271

272-
In addition, the following C math functions are also supported:
272+
The following C math functions are also supported:
273273

274274
- 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
275275

276+
The following functions are also built-in and provided by TinyExpr:
277+
278+
- fac (factorials e.g. `fac 5` == 120)
279+
- ncr (combinations e.g. `ncr(6,2)` == 15)
280+
- npr (permutations e.g. `npr(6,2)` == 30)
281+
276282
Also, the following constants are available:
277283

278284
- `pi`, `e`
279285

280286

281-
##Compile-time options
287+
## Compile-time options
282288

283289

284-
By default, TinyExpr does exponentation from left to right. For example:
290+
By default, TinyExpr does exponentiation from left to right. For example:
285291

286292
`a^b^c == (a^b)^c` and `-a^b == (-a)^b`
287293

288294
This is by design. It's the way that spreadsheets do it (e.g. Excel, Google Sheets).
289295

290296

291-
If you would rather have exponentation work from right to left, you need to
297+
If you would rather have exponentiation work from right to left, you need to
292298
define `TE_POW_FROM_RIGHT` when compiling `tinyexpr.c`. There is a
293299
commented-out define near the top of that file. With this option enabled, the
294300
behaviour is:
@@ -300,7 +306,7 @@ That will match how many scripting languages do it (e.g. Python, Ruby).
300306
Also, if you'd like `log` to default to the natural log instead of `log10`,
301307
then you can define `TE_NAT_LOG`.
302308

303-
##Hints
309+
## Hints
304310

305311
- All functions/types start with the letters *te*.
306312

test.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,13 @@ void test_nans() {
213213
"1%0",
214214
"1%(1%0)",
215215
"(1%0)%1",
216+
"fac(-1)",
217+
"ncr(2, 4)",
218+
"ncr(-2, 4)",
219+
"ncr(2, -4)",
220+
"npr(2, 4)",
221+
"npr(-2, 4)",
222+
"npr(2, -4)",
216223
};
217224

218225
int i;
@@ -234,6 +241,40 @@ void test_nans() {
234241
}
235242

236243

244+
void test_infs() {
245+
246+
const char *infs[] = {
247+
"1/0",
248+
"log(0)",
249+
"pow(2,10000000)",
250+
"fac(300)",
251+
"ncr(300,100)",
252+
"ncr(300000,100)",
253+
"ncr(300000,100)*8",
254+
"npr(3,2)*ncr(300000,100)",
255+
"npr(100,90)",
256+
"npr(30,25)",
257+
};
258+
259+
int i;
260+
for (i = 0; i < sizeof(infs) / sizeof(const char *); ++i) {
261+
const char *expr = infs[i];
262+
263+
int err;
264+
const double r = te_interp(expr, &err);
265+
lequal(err, 0);
266+
lok(r == r + 1);
267+
268+
te_expr *n = te_compile(expr, 0, 0, &err);
269+
lok(n);
270+
lequal(err, 0);
271+
const double c = te_eval(n);
272+
lok(c == c + 1);
273+
te_free(n);
274+
}
275+
}
276+
277+
237278
void test_variables() {
238279

239280
double x, y, test;
@@ -587,17 +628,63 @@ void test_pow() {
587628

588629
}
589630

631+
void test_combinatorics() {
632+
test_case cases[] = {
633+
{"fac(0)", 1},
634+
{"fac(0.2)", 1},
635+
{"fac(1)", 1},
636+
{"fac(2)", 2},
637+
{"fac(3)", 6},
638+
{"fac(4.8)", 24},
639+
{"fac(10)", 3628800},
640+
641+
{"ncr(0,0)", 1},
642+
{"ncr(10,1)", 10},
643+
{"ncr(10,0)", 1},
644+
{"ncr(10,10)", 1},
645+
{"ncr(16,7)", 11440},
646+
{"ncr(16,9)", 11440},
647+
{"ncr(100,95)", 75287520},
648+
649+
{"npr(0,0)", 1},
650+
{"npr(10,1)", 10},
651+
{"npr(10,0)", 1},
652+
{"npr(10,10)", 3628800},
653+
{"npr(20,5)", 1860480},
654+
{"npr(100,4)", 94109400},
655+
};
656+
657+
658+
int i;
659+
for (i = 0; i < sizeof(cases) / sizeof(test_case); ++i) {
660+
const char *expr = cases[i].expr;
661+
const double answer = cases[i].answer;
662+
663+
int err;
664+
const double ev = te_interp(expr, &err);
665+
lok(!err);
666+
lfequal(ev, answer);
667+
668+
if (err) {
669+
printf("FAILED: %s (%d)\n", expr, err);
670+
}
671+
}
672+
}
673+
674+
590675
int main(int argc, char *argv[])
591676
{
592677
lrun("Results", test_results);
593678
lrun("Syntax", test_syntax);
594679
lrun("NaNs", test_nans);
680+
lrun("INFs", test_infs);
595681
lrun("Variables", test_variables);
596682
lrun("Functions", test_functions);
597683
lrun("Dynamic", test_dynamic);
598684
lrun("Closure", test_closure);
599685
lrun("Optimize", test_optimize);
600686
lrun("Pow", test_pow);
687+
lrun("Combinatorics", test_combinatorics);
601688
lresults();
602689

603690
return lfails != 0;

tinyexpr.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,17 @@ For log = natural log uncomment the next line. */
3939
#include <math.h>
4040
#include <string.h>
4141
#include <stdio.h>
42+
#include <limits.h>
4243

4344
#ifndef NAN
4445
#define NAN (0.0/0.0)
4546
#endif
4647

48+
#ifndef INFINITY
49+
#define INFINITY (1.0/0.0)
50+
#endif
51+
52+
4753
typedef double (*te_fun2)(double, double);
4854

4955
enum {
@@ -113,6 +119,35 @@ void te_free(te_expr *n) {
113119

114120
static double pi() {return 3.14159265358979323846;}
115121
static double e() {return 2.71828182845904523536;}
122+
static double fac(double a) {/* simplest version of fac */
123+
if (a < 0.0)
124+
return NAN;
125+
if (a > UINT_MAX)
126+
return INFINITY;
127+
unsigned int ua = (unsigned int)(a);
128+
unsigned long int result = 1, i;
129+
for (i = 1; i <= ua; i++) {
130+
if (i > ULONG_MAX / result)
131+
return INFINITY;
132+
result *= i;
133+
}
134+
return (double)result;
135+
}
136+
static double ncr(double n, double r) {
137+
if (n < 0.0 || r < 0.0 || n < r) return NAN;
138+
if (n > UINT_MAX || r > UINT_MAX) return INFINITY;
139+
unsigned long int un = (unsigned int)(n), ur = (unsigned int)(r), i;
140+
unsigned long int result = 1;
141+
if (ur > un / 2) ur = un - ur;
142+
for (i = 1; i <= ur; i++) {
143+
if (result > ULONG_MAX / (un - ur + i))
144+
return INFINITY;
145+
result *= un - ur + i;
146+
result /= i;
147+
}
148+
return result;
149+
}
150+
static double npr(double n, double r) {return ncr(n, r) * fac(r);}
116151

117152
static const te_variable functions[] = {
118153
/* must be in alphabetical order */
@@ -126,6 +161,7 @@ static const te_variable functions[] = {
126161
{"cosh", cosh, TE_FUNCTION1 | TE_FLAG_PURE, 0},
127162
{"e", e, TE_FUNCTION0 | TE_FLAG_PURE, 0},
128163
{"exp", exp, TE_FUNCTION1 | TE_FLAG_PURE, 0},
164+
{"fac", fac, TE_FUNCTION1 | TE_FLAG_PURE, 0},
129165
{"floor", floor, TE_FUNCTION1 | TE_FLAG_PURE, 0},
130166
{"ln", log, TE_FUNCTION1 | TE_FLAG_PURE, 0},
131167
#ifdef TE_NAT_LOG
@@ -134,6 +170,8 @@ static const te_variable functions[] = {
134170
{"log", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0},
135171
#endif
136172
{"log10", log10, TE_FUNCTION1 | TE_FLAG_PURE, 0},
173+
{"ncr", ncr, TE_FUNCTION2 | TE_FLAG_PURE, 0},
174+
{"npr", npr, TE_FUNCTION2 | TE_FLAG_PURE, 0},
137175
{"pi", pi, TE_FUNCTION0 | TE_FLAG_PURE, 0},
138176
{"pow", pow, TE_FUNCTION2 | TE_FLAG_PURE, 0},
139177
{"sin", sin, TE_FUNCTION1 | TE_FLAG_PURE, 0},

0 commit comments

Comments
 (0)