fasteval-0.2.4/.gitignore010066400017500001750000000005301355116440700135410ustar0000000000000000# Generated by Cargo # will have compiled files and executables /target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk # vim swap files *.swp fasteval-0.2.4/CHANGELOG.md010066400017500001750000000031641361311225400133600ustar0000000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] (Click the above link to see the work that has occurred since the latest release.) ## [0.2.4] - 2020-01-26 ### Added - Convenience type aliases, to make creation of complex namespace types easier: StringToF64Namespace, StrToF64Namespace, StringToCallbackNamespace, StrToCallbackNamespace, LayeredStringToF64Namespace ## [0.2.3] - 2020-01-17 ### Added - Examples in the 'examples' directory. - Improved documentation. ## [0.2.2] - 2020-01-15 ### Removed - Removed the `fasteval::parse()` convenience function. Now use the `Parser` directly: `Parser::new().parse()`. This was done to improve the usability of custom safety parse limits. ## [0.2.1] - 2020-01-14 ### Added - Enable custom safety parse limits. - Better documentation of safety features. ## [0.2.0] - 2020-01-01 This was the initial public release. Changes before this point are not described here, but they can still be viewed in the [Repository]. [Unreleased]: https://github.com/likebike/fasteval/compare/0.2.4...HEAD [0.2.4]: https://github.com/likebike/fasteval/compare/0.2.3...0.2.4 [0.2.3]: https://github.com/likebike/fasteval/compare/0.2.2...0.2.3 [0.2.2]: https://github.com/likebike/fasteval/compare/0.2.1...0.2.2 [0.2.1]: https://github.com/likebike/fasteval/compare/0.2.0...0.2.1 [0.2.0]: https://github.com/likebike/fasteval/releases/tag/0.2.0 [Repository]: https://github.com/likebike/fasteval fasteval-0.2.4/Cargo.toml.orig010066400017500001750000000013461361304056100144370ustar0000000000000000[package] name = "fasteval" version = "0.2.4" authors = ["Christopher Sebastian "] license = "MIT" readme = "README.md" repository = "https://github.com/likebike/fasteval" documentation = "https://docs.rs/fasteval/" description = """Fast evaluation of algebraic expressions""" keywords = ["evaluate", "math", "algebra", "calculate", "expression"] categories = ["mathematics", "science", "parser-implementations"] edition = "2018" [dependencies] [profile.bench] debug = true lto = true [features] default = ["alpha-keywords"] alpha-keywords = [] # Enable 'NaN', 'inf', 'and', 'or' unsafe-vars = [] # tinyexpr-style pointer-based variables. nightly = [] # Enable features that depend on Rust nightly. fasteval-0.2.4/Cargo.toml0000644000000021441361311252600107410ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "fasteval" version = "0.2.4" authors = ["Christopher Sebastian "] description = "Fast evaluation of algebraic expressions" documentation = "https://docs.rs/fasteval/" readme = "README.md" keywords = ["evaluate", "math", "algebra", "calculate", "expression"] categories = ["mathematics", "science", "parser-implementations"] license = "MIT" repository = "https://github.com/likebike/fasteval" [profile.bench] lto = true debug = true [dependencies] [features] alpha-keywords = [] default = ["alpha-keywords"] nightly = [] unsafe-vars = [] fasteval-0.2.4/Cargo.toml.orig0000644000000013461361311252600117030ustar00[package] name = "fasteval" version = "0.2.4" authors = ["Christopher Sebastian "] license = "MIT" readme = "README.md" repository = "https://github.com/likebike/fasteval" documentation = "https://docs.rs/fasteval/" description = """Fast evaluation of algebraic expressions""" keywords = ["evaluate", "math", "algebra", "calculate", "expression"] categories = ["mathematics", "science", "parser-implementations"] edition = "2018" [dependencies] [profile.bench] debug = true lto = true [features] default = ["alpha-keywords"] alpha-keywords = [] # Enable 'NaN', 'inf', 'and', 'or' unsafe-vars = [] # tinyexpr-style pointer-based variables. nightly = [] # Enable features that depend on Rust nightly. fasteval-0.2.4/LICENSE010066400017500001750000000020661354770346300125720ustar0000000000000000MIT License Copyright (c) 2019 Christopher Sebastian Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fasteval-0.2.4/README.md010066400017500001750000000154771361304056700130470ustar0000000000000000# fasteval Fast and safe evaluation of algebraic expressions `fasteval` is a library for parsing, compiling, and evaluating algebraic expressions. It can be used directly as a calculator language (much like `python`), and it is an excellent foundation for building higher-level-languages. Documentation: * [API Reference (docs.rs)](https://docs.rs/fasteval/) ## Usage Add this to your Cargo.toml: [dependencies] fasteval = "0.2.4" You should **always** build with `RUSTFLAGS="--emit=asm"` because [it greatly improves LLVM's compile-time optimizations](http://likebike.com/posts/How_To_Write_Fast_Rust_Code.html#emit-asm). If you are using a 'nightly' Rust compiler, you can build with `--features nightly` to enable optimizations that aren't yet available in 'stable' Rust. You can build with `--no-default-features` to disable alphabetical keywords like `and`, `or`, `NaN`, `inf`. (These words might be important to your applications.) You can build with `--features unsafe-vars` to enable [Unsafe Variables](https://docs.rs/fasteval/#unsafe-variables). ## Features * No dependencies. * Safe execution of untrusted expressions. * Works with stable Rust. * Supports interpretation (i.e. parse & eval) as well as compiled execution (i.e. parse, compile, eval). * Supports Variables and Custom Functions. * `fasteval` is a good base for building higher-level languages. * Supports many built-in functions and constants. * Supports all the standard algebraic unary and binary operators (+ - * / ^ %), as well as comparisons (< <= == != >= >) and logical operators (&& ||) with short-circuit support. * Easy integration into many different types of applications, including scoped evaluation. * Very fast performance. ## Easy Example Here is one simple example. See the [API Reference](https://docs.rs/fasteval/#examples) for many more! The `ez_eval()` function performs the entire allocation-parse-eval process for you. It is slightly inefficient because it always allocates a fresh [`Slab`](https://docs.rs/fasteval/latest/fasteval/slab/index.html), but it is very simple to use: ```rust fn main() -> Result<(), fasteval::Error> { // This example doesn't use any variables, so just use an EmptyNamespace: let mut ns = fasteval::EmptyNamespace; let val = fasteval::ez_eval( "1+2*3/4^5%6 + log(100K) + log(e(),100) + [3*(3-3)/3] + (2<3) && 1.23", &mut ns)?; // | | | | | | | | // | | | | | | | boolean logic with short-circuit support // | | | | | | comparisons // | | | | | square-brackets act like parenthesis // | | | | built-in constants: e(), pi() // | | | 'log' can take an optional first 'base' argument, defaults to 10 // | | numeric literal with suffix: p, n, µ, m, K, M, G, T // | many built-in functions: print, int, ceil, floor, abs, sign, log, round, min, max, sin, asin, ... // standard binary operators assert_eq!(val, 1.23); Ok(()) } ``` ## REPL Demo ```text github.com/likebike/fasteval$ rlwrap cargo run --release --example repl Finished release [optimized] target(s) in 0.01s Running `target/release/examples/repl` >>> print("Hello fasteval", 1, 2, 3) Hello fasteval 1 2 3 3 >>> _ + 1 4 >>> _ + 1 5 >>> _ * 2 10 >>> _ ^ 0.5 3.1622776601683795 >>> let a = 1 1 >>> let b = a + 1 2 >>> let c = a + b * 3 7 >>> a + b + c 10 >>> push Entered scope[1] >>> let b = b + 10 12 >>> a + b + c 20 >>> pop Exited scope[1] >>> a + b + c 10 >>> 1+2*3/4^5%6 + log(100K) + log(e(),100) + [3*(3-3)/3] + (2<3) && 1.23 1.23 >>> 1+2*3/4^5%6 + print("log(100K) =",log(100K)) + log(e(),100) + [3*(3-3)/3] + (2<3) && 1.23 log(100K) = 5 1.23 ``` ## Safety `fasteval` is designed to evaluate untrusted expressions safely. By default, an expression can only perform math operations; there is no way for it to access other types of operations (like network or filesystem or external commands). Additionally, we guard against malicious expressions: * Expressions that are too large (greater than 4KB). * Expressions that are too-deeply nested (greater than 32 levels). * Expressions with too many values (greater than 64). * Expressions with too many sub-expressions (greater than 64). All limits can be customized at parse time. If any limits are exceeded, [`parse()`](https://docs.rs/fasteval/latest/fasteval/parser/struct.Parser.html#method.parse) will return an [Error](https://docs.rs/fasteval/latest/fasteval/error/enum.Error.html). Note that it *is* possible for you (the developer) to define custom functions which might perform dangerous operations. It is your responsibility to make sure that all custom functionality is safe. ## Performance Benchmarks Here is a short summary of the performance benchmarks. For a more complete report and anlysis, see the [official documentation](https://docs.rs/fasteval/#performance-benchmarks). ### Charts Note that the following charts use logarithmic scales. Therefore, tiny visual differences actually represent very significant performance differences. **Performance of evaluation of a compiled expression:** ![Compiled Eval Performance](https://raw.githubusercontent.com/likebike/fasteval/master/benches/results/20191225/fasteval-compiled.png) **Performance of one-time interpretation (parse and eval):** ![Interpretation Performance](https://raw.githubusercontent.com/likebike/fasteval/master/benches/results/20191225/fasteval-interp.png) **Performance of compiled Unsafe Variables, compared to the tinyexpr C library (the only other library in our test set that supports this mode):** ![Unsafe Compiled Eval Performance](https://raw.githubusercontent.com/likebike/fasteval/master/benches/results/20191225/fasteval-compiled-unsafe.png) **Performance of interpreted Unsafe Variables, compared to the tinyexpr C library (the only other library in our test set that supports this mode):** ![Unsafe Interpretation Performance](https://raw.githubusercontent.com/likebike/fasteval/master/benches/results/20191225/fasteval-interp-unsafe.png) ### Summary The impressive thing about these results is that `fasteval` consistently achieves the fastest times across every benchmark and in every mode of operation (interpreted, compiled, and unsafe). It's easy to create a design to claim the #1 spot in any one of these metrics by sacrificing performance in another, but it is difficult to create a design that can be #1 across-the-board. Because of the broad and robust performance advantages, `fasteval` is very likely to be an excellent choice for your dynamic evaluation needs. ## License `fasteval` is distributed under the terms the MIT license. See [LICENSE](https://github.com/likebike/fasteval/blob/master/LICENSE) for details. fasteval-0.2.4/benches/bench.py010077500017500001750000000010241360146220700146060ustar0000000000000000#!/usr/bin/env python3 import time import statistics def calc(): for i in range(100): x = 3.0 * (3.0 + 3.0) / 3.0 stats = [] for i in range(100): start = time.time() calc() stats.append((time.time()-start)*1_000_000_000) avg = sum(stats)/len(stats) stdev = statistics.stdev(stats, avg) print("python_eval_only_100x:", int(avg), "ns +/-", int(stdev)) print("It's very difficult to estimate the parse time in a fair way. (Maybe 'time' the run of a PYC? But that includes many proc startup costs...)") fasteval-0.2.4/benches/bench.rs.tmpl010066400017500001750000001204651361005771500155710ustar0000000000000000// https://easyperf.net/blog/2019/08/02/Perf-measurement-environment-on-Linux // Here is how I run benchmarks: // for F in /sys/devices/system/cpu/cpufreq/policy*/scaling_governor; do echo $F; cat $F; done // for F in /sys/devices/system/cpu/cpufreq/policy*/scaling_governor; do echo performance >$F; done // // while true; do echo "time: $(date +%s)"; cat benches/bench.rs.tmpl | sed "s|//SHIFT_CODE|$( N=$(( 1 + $RANDOM % 1024 )); while [[ $N > 0 ]]; do N=$(( $N - 1 )); echo -n 'let x=black_box(x+1);'; done )|g" >benches/bench.rs; RUSTFLAGS="--emit=asm" cargo bench; done >bench.out // while true; do echo "time: $(date +%s)"; cat benches/bench.rs.tmpl | sed "s|//SHIFT_CODE|$( N=$(( 1 + $RANDOM % 1024 )); while [[ $N > 0 ]]; do N=$(( $N - 1 )); echo -n 'let x=black_box(x+1);'; done )|g" >benches/bench.rs; RUSTFLAGS="--emit=asm" cargo bench --features unsafe-vars; done >bench.out // cat bench.out | awk -v "now=$(date +%s)" '$1=="time:"{when=$2} $3=="..." && $4=="bench:" {gsub(/,/, "", $5); v=$5+0; if (t[$2]=="" || v 73.8 ns/op // // "3 * 3 - 3 / 3" // user@asus:~$ ( echo 'x=[0]'; echo 'for i in range(100000000):'; echo ' x[0]=3 * 3 - 3 / 3'; echo 'print(x)') | time python3 // 7.20user 0.00system 0:07.21elapsed --> 72.1 ns/op // // "2 ^ 3 ^ 4" = 2417851639229258349412352 // user@asus:~$ ( echo 'x=[0]'; echo 'for i in range(100000000):'; echo ' x[0]=2**3**4'; echo 'print(x)') | time python3 // 39.55user 0.00system 0:39.55elapsed --> 395.5 ns/op // // "x * 2" // user@asus:~$ ( echo '_,x,y,z=[0],1,2,3'; echo 'for i in range(100000000):'; echo ' _[0]=x*2'; echo 'print(_)') | time python3 // 10.14user 0.00system 0:10.14elapsed --> 101.4 ns/op // // "sin(x)" // user@asus:~$ ( echo 'import math'; echo '_,x,y,z=[0],1,2,3'; echo 'for i in range(100000000):'; echo ' _[0]=math.sin(x)'; echo 'print(_)') | time python3 // 19.67user 0.00system 0:19.70elapsed --> 197 ns/op // // "(-z + (z^2 - 4*x*y)^0.5) / (2*x)" // user@asus:~$ ( echo '_,x,y,z=[0],1,2,3'; echo 'for i in range(100000000):'; echo ' _[0]=(-z + (z**2 - 4*x*y)**0.5) / (2*x)'; echo 'print(_)') | time python3 // 56.92user 0.00system 0:56.92elapsed --> 569 ns/op // // "((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90))))" // user@asus:~$ ( echo '_,x,y,z=[0],1,2,3'; echo 'for i in range(100000000):'; echo ' _[0]=((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90))))'; echo 'print(_)') | time python3 // 7.24user 0.01system 0:07.26elapsed --> 72.6 ns/op // // // bc: // user@asus:~$ echo 'for (i=0; i<1000000; i++) { (3 * (3 + 3) / 3) }' | time bc >/dev/null // 1.71user 0.32system 0:02.04elapsed --> 2040 ns/op // // user@asus:~$ echo 'for (i=0; i<1000000; i++) { 3*3-3/3 }' | time bc >/dev/null // 1.43user 0.22system 0:01.66elapsed --> 1660 ns/op // // user@asus:~$ echo 'for (i=0; i<1000000; i++) { 2 ^ 3 ^ 4 }' | time bc >/dev/null = 2417851639229258349412352 // 2.33user 0.21system 0:02.55elapsed --> 2550 ns/op // // user@asus:~$ echo 'x=1; for (i=0; i<1000000; i++) { x * 2 }' | time bc >/dev/null // 0.74user 0.27system 0:01.01elapsed --> 1010 ns/op // // user@asus:~$ echo 'x=1; for (i=0; i<1000000; i++) { s(x) }' | time bc -l >/dev/null // 40.82user 0.40system 0:41.24elapsed --> 41240 ns/op // // user@asus:~$ echo 'x=1; y=2; z=3; for (i=0; i<1000000; i++) { (-z + sqrt(z^2 - 4*x*y)) / (2*x) }' | time bc >/dev/null // 1.93user 0.27system 0:02.20elapsed --> 2200 ns/op // // user@asus:~$ echo 'for (i=0; i<1000000; i++) { ((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90)))) }' | time bc >/dev/null // 10.95user 0.30system 0:11.26elapsed --> 11260 ns/op // // // caldyn: // "(3 * (3 + 3) / 3)", No Context // test ez ... bench: 1,191 ns/iter (+/- 315) // test preparse_precompile_eval_1000x ... bench: 4,193 ns/iter (+/- 217) // // "(3 * (3 + 3) / 3)", Normal Context // test ez ... bench: 1,298 ns/iter (+/- 70) // test preparse_precompile_eval_1000x ... bench: 4,273 ns/iter (+/- 233) // // "(3 * (3 + 3) / 3)", Callback Context // test ez ... bench: 1,286 ns/iter (+/- 158) // test preparse_precompile_eval_1000x ... bench: 4,223 ns/iter (+/- 236) // // "3 * 3 - 3 / 3", Callback Context // test ez ... bench: 1,070 ns/iter (+/- 80) // test preparse_precompile_eval_1000x ... bench: 4,245 ns/iter (+/- 190) // // "2 ^ 3 ^ 4", = 2417851639229258300000000.0, Callback Context // test ez ... bench: 867 ns/iter (+/- 75) // test preparse_precompile_eval_1000x ... bench: 4,182 ns/iter (+/- 238) // // "x * 2", Callback Context // test ez ... bench: 607 ns/iter (+/- 61) // test preparse_precompile_eval_1000x ... bench: 77,540 ns/iter (+/- 12,490) // // "sin(x)", Callback Context // test ez ... bench: 573 ns/iter (+/- 54) // test preparse_precompile_eval_1000x ... bench: 97,861 ns/iter (+/- 6,063) // // "(-z + (z^2 - 4*x*y)^0.5) / (2*x)" --> -z => 0 - z // test ez ... bench: 4,440 ns/iter (+/- 618) // test preparse_precompile_eval_1000x ... bench: 525,066 ns/iter (+/- 64,388) // // "((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90))))" // test ez ... bench: 24,598 ns/iter (+/- 4,140) // test preparse_precompile_eval_1000x ... bench: 4,418 ns/iter (+/- 429) // // // tinyexpr-rs: // "(3 * (3 + 3) / 3)" // test bench_interp ... bench: 1,171 ns/iter (+/- 120) // // "3 * 3 - 3 / 3" // test bench_interp ... bench: 895 ns/iter (+/- 50) // // "2 ^ (3 ^ 4)" = 2417851639229258300000000 // test bench_interp ... bench: 816 ns/iter (+/- 83) // // "((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90))))" // test bench_interp ... bench: 38,422 ns/iter (+/- 6,510) // // // tinyexpr-c: // "(3 * (3 + 3) / 3)" // te_interp : 748 ns/iter // parse_compile_eval : 762 ns/iter // preparse_precompile_eval : 2.8 ns/iter // // "3 * 3 - 3 / 3" // te_interp : 615 ns/iter // parse_compile_eval : 630 ns/iter // preparse_precompile_eval : 2.8 ns/iter // // "2 ^ (3 ^ 4)" = 2417851639229258349412352.000000 // te_interp : 585 ns/iter // parse_compile_eval : 580 ns/iter // preparse_precompile_eval : 2.8 ns/iter // // "x * 2" // parse_compile_eval : 221 ns/iter // preparse_precompile_eval : 9.4 ns/iter // // "sin(x)" // parse_compile_eval : 249 ns/iter // preparse_precompile_eval : 21.4 ns/iter // // "(-z + sqrt(z^2 - 4*x*y)) / (2*x)" // parse_compile_eval : 1507 ns/iter // preparse_precompile_eval : 117 ns/iter // // "((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90))))" // te_interp : 12,423 ns/iter // parse_compile_eval : 12,222 ns/iter // preparse_precompile_eval : 2.8 ns/iter // // // calc: // "(3 * (3 + 3) / 3)" // test eval_1000x ... bench: 1,675,179 ns/iter (+/- 295,930) // // "3 * 3 - 3 / 3" // test eval_1000x ... bench: 1,445,273 ns/iter (+/- 210,599) // // "2 ** 3 ** 4" = 2417851639229258349412352 // test eval_1000x ... bench: 2,275,338 ns/iter (+/- 351,933) // // "x * 2" // test eval_1000x ... bench: 792,132 ns/iter (+/- 145,850) // // "sin(x)" // N/A // // "(-z + (z^2 - 4*x*y)^0.5) / (2*x)" // test eval_1000x ... bench: 26,565,727 ns/iter (+/- 3,870,655) // // "((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90))))" // test eval_1000x ... bench: 44,810,253 ns/iter (+/- 5,380,532) // // // meval: // "(3 * (3 + 3) / 3)" // test parse_eval ... bench: 3,341 ns/iter (+/- 254) // test preparse_eval ... bench: 1,482 ns/iter (+/- 121) // // "3 * 3 - 3 / 3" // test parse_eval ... bench: 2,630 ns/iter (+/- 332) // test preparse_eval ... bench: 1,564 ns/iter (+/- 187) // // "2 ^ 3 ^ 4" = 2417851639229258300000000 // test parse_eval ... bench: 2,622 ns/iter (+/- 352) // test preparse_eval ... bench: 1,683 ns/iter (+/- 319) // // "x * 2" // test parse_eval ... bench: 2,289 ns/iter (+/- 344) // test preparse_eval ... bench: 1,484 ns/iter (+/- 80) // // "sin(x)" // test parse_eval ... bench: 2,476 ns/iter (+/- 323) // test preparse_eval ... bench: 1,521 ns/iter (+/- 166) // // "(-z + (z^2 - 4*x*y)^0.5) / (2*x)" // test parse_eval ... bench: 5,830 ns/iter (+/- 641) // test preparse_eval ... bench: 1,803 ns/iter (+/- 471) // // "((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90))))" // test parse_eval ... bench: 25,371 ns/iter (+/- 8,285) // test preparse_eval ... bench: 2,642 ns/iter (+/- 163) // // // rsc: // "(3 * (3 + 3) / 3)" // test ez ... bench: 1,438 ns/iter (+/- 130) // test parse_eval ... bench: 1,434 ns/iter (+/- 98) // test preparse_eval ... bench: 92 ns/iter (+/- 16) // // "3 * 3 - 3 / 3" // test ez ... bench: 1,291 ns/iter (+/- 150) // test parse_eval ... bench: 1,330 ns/iter (+/- 464) // test preparse_eval ... bench: 114 ns/iter (+/- 11) // // "2 ^ (3 ^ 4)" = 2417851639229258300000000 // test ez ... bench: 1,283 ns/iter (+/- 141) // test parse_eval ... bench: 1,306 ns/iter (+/- 113) // test preparse_eval ... bench: 244 ns/iter (+/- 165) // // "x * 2" // test ez ... N/A // test parse_eval ... bench: 1,962 ns/iter (+/- 150) // test preparse_eval ... bench: 117 ns/iter (+/- 26) // // "sin(x)" // test ez ... N/A // test parse_eval ... bench: 2,262 ns/iter (+/- 385) // test preparse_eval ... bench: 158 ns/iter (+/- 22) // // "(-z + (z^2 - 4*x*y)^0.5) / (2*x)" // test ez ... N/A // test parse_eval ... bench: 5,808 ns/iter (+/- 499) // test preparse_eval ... bench: 370 ns/iter (+/- 103) // // "((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90))))" // test ez ... bench: 20,343 ns/iter (+/- 2,515) // test parse_eval ... bench: 24,555 ns/iter (+/- 6,041) // test preparse_eval ... bench: 1,491 ns/iter (+/- 146) #![feature(test)] extern crate test; // 'extern crate' seems to be required for this scenario: https://github.com/rust-lang/rust/issues/57288 use test::{Bencher, black_box}; use fasteval::{Parser, Compiler, Evaler, Slab, EmptyNamespace, CachedCallbackNamespace, ez_eval, eval_compiled, eval_compiled_ref}; use std::collections::BTreeMap; use std::f64::NAN; //fn evalcb(_:&str) -> Option { None } fn evalcb(name:&str, args:Vec) -> Option { match name { "x" => Some(1.0), "y" => Some(2.0), "z" => Some(3.0), "foo" => Some(args.get(0).unwrap_or(&NAN)*10.0), "bar" => Some(args.get(0).unwrap_or(&NAN) + args.get(1).unwrap_or(&NAN)), _ => None, } } macro_rules! Namespace { () => { { let mut map = BTreeMap::new(); map.insert("x".to_string(), 1.0); map.insert("y".to_string(), 2.0); map.insert("z".to_string(), 3.0); map } //EmptyNamespace //CachedCallbackNamespace::new(evalcb) //CachedLayeredNamespace::new(evalcb) } } macro_rules! memshift { () => { { let x = black_box(0); let x = black_box(x+1); //SHIFT_CODE black_box(x); // Silence 'unused variable' warning. } } } //static EXPR : &'static str = "(3 * (3 + 3) / 3)"; //static EXPR : &'static str = "3 * 3 - 3 / 3"; //static EXPR : &'static str = "2 ^ 3 ^ 4"; //static EXPR : &'static str = "x * 2"; //static EXPR : &'static str = "sin(x)"; //static EXPR : &'static str = "(-z + (z^2 - 4*x*y)^0.5) / (2*x)"; static EXPR : &'static str = "((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90))))"; #[bench] fn native_1000x(bencher:&mut Bencher) { memshift!(); // Silence compiler warnings about unused imports: let _ = EmptyNamespace; let _ = CachedCallbackNamespace::new(|_,_| None); #[allow(dead_code)] fn x() -> f64 { black_box(1.0) } #[allow(unused_variables)] let (a,b,c) = (1.0f64, 3.0f64, 2.0f64); bencher.iter(|| { //let (a,b,c) = (a,b,c); // Localize for _ in 0..1000 { //black_box(3.0 * (3.0 + 3.0) / 3.0); //black_box(3.0 * 3.0 - 3.0 / 3.0); //black_box(2.0f64.powf(3.0).powf(4.0)); //black_box(x() * 2.0); //black_box(x().sin()); //black_box( (-b + (b.powf(2.0) - 4.0*a*c).powf(0.5)) / (2.0*a) ); black_box( ((((87.))) - 73.) + (97. + (((15. / 55. * ((31.)) + 35.))) + (15. - (9.)) - (39. / 26.) / 20. / 91. + 27. / (33. * 26. + 28. - (7.) / 10. + 66. * 6.) + 60. / 35. - ((29.) - (69.) / 44. / (92.)) / (89.) + 2. + 87. / 47. * ((2.)) * 83. / 98. * 42. / (((67.)) * ((97.))) / (34. / 89. + 77.) - 29. + 70. * (20.)) + ((((((92.))) + 23. * (98.) / (95.) + (((99.) * (41.))) + (5. + 41.) + 10.) - (36.) / (6. + 80. * 52. + (90.)))) ); } }); } #[bench] fn ez(b:&mut Bencher) { memshift!(); let mut vars=BTreeMap::new(); vars.insert("x".to_string(),1.0); vars.insert("y".to_string(),2.0); vars.insert("z".to_string(),3.0); b.iter(|| { black_box(match ez_eval(EXPR, &mut vars) { Ok(f) => f, Err(_) => 0.0, }); }); } #[bench] fn parse_eval_1000x(b:&mut Bencher) { memshift!(); let parser = Parser::new(); let mut slab = Slab::new(); let mut ns = Namespace!(); b.iter(|| { let _ = (|| -> Result<(),fasteval::Error> { for _ in 0..1000 { black_box(parser.parse(EXPR, &mut slab.ps)?.from(&slab.ps).eval(&slab, &mut ns)?); } Ok(()) })(); }); } //// Commented until we bring CachedLayeredNamespace back. // #[bench] // fn parse_nsbubble_eval_1000x(b:&mut Bencher) { // memshift!(); // // let parser = Parser::new(); // let mut slab = Slab::new(); // let mut ns = CachedLayeredNamespace::new(evalcb); // // b.iter(|| { // let _ = (|| -> Result<(),fasteval::Error> { // for _ in 0..1000 { // let expr_ref = parser.parse(EXPR, &mut slab.ps)?.from(&slab.ps); // let mut bub = Bubble::new(&mut ns); bub.push(); // black_box( expr_ref.eval(&slab, &mut bub)? ); // } // Ok(()) // })(); // }); // } #[bench] #[cfg(feature="unsafe-vars")] fn parse_eval_unsafe_1000x(b:&mut Bencher) { memshift!(); let parser = Parser::new(); let mut slab = Slab::new(); let x = 1.0; let y = 2.0; let z = 3.0; let foo = 0.0; let bar = 0.0; unsafe { slab.ps.add_unsafe_var("x".to_string(), &x); slab.ps.add_unsafe_var("y".to_string(), &y); slab.ps.add_unsafe_var("z".to_string(), &z); slab.ps.add_unsafe_var("foo".to_string(), &foo); slab.ps.add_unsafe_var("bar".to_string(), &bar); } let mut ns = EmptyNamespace; b.iter(|| { let _ = (|| -> Result<(),fasteval::Error> { for _ in 0..1000 { black_box(parser.parse(EXPR, &mut slab.ps)?.from(&slab.ps).eval(&slab, &mut ns)?); } Ok(()) })(); }); } #[bench] fn preparse_eval_1000x(b:&mut Bencher) { memshift!(); let mut slab = Slab::new(); let mut ns = Namespace!(); let expr_ref = match Parser::new().parse(EXPR, &mut slab.ps) { Ok(expr_i) => expr_i.from(&slab.ps), Err(_) => return, }; b.iter(|| { let _ = (|| -> Result<(),fasteval::Error> { for _ in 0..1000 { black_box( expr_ref.eval(&slab, &mut ns)? ); } Ok(()) })(); }); } #[bench] fn parse_compile_eval_1000x(b:&mut Bencher) { memshift!(); let parser = Parser::new(); let mut slab = Slab::new(); let mut ns = Namespace!(); b.iter(|| { let _ = (|| -> Result<(),fasteval::Error> { for _ in 0..1000 { let instr = parser.parse(EXPR, &mut slab.ps)?.from(&slab.ps).compile(&slab.ps, &mut slab.cs); black_box(eval_compiled!(instr, &slab, &mut ns)); } Ok(()) })(); }); } #[bench] fn preparse_precompile_eval_1000x(b:&mut Bencher) { memshift!(); let mut slab = Slab::new(); let mut ns = Namespace!(); let instr = match Parser::new().parse_noclear(EXPR, &mut slab.ps) { Ok(expr_i) => expr_i.from(&slab.ps).compile(&slab.ps, &mut slab.cs), Err(_) => return, }; b.iter(|| { let _ = (|| -> Result<(),fasteval::Error> { let (instr_ref, slab_ref, ns_mut) = (&instr, &slab, &mut ns); // Localize (doesn't help much) for _ in 0..1000 { black_box( eval_compiled_ref!(instr_ref, slab_ref, ns_mut)); } Ok(()) })(); }); //// Produces basically the same results, proving that the --emit=asm performanace boost is not coming from this test function -- it's coming from the evaluation, and I'm not able to replicate it. // let _ = (|| -> Result<(),fasteval::Error> { // let mut slab = Slab::new(); // let mut ns = Namespace!(); // let instr = match parse_noclear(EXPR, &mut slab.ps) { // Ok(expr_i) => expr_i.from(&slab.ps).compile(&slab.ps, &mut slab.cs), // Err(e) => return Err(e), // }; // // let start = std::time::Instant::now(); // for _ in 0..1_000_000 { // black_box( eval_compiled_ref!(&instr, &slab, &mut ns) ); // } // eprintln!("bench time: {}", start.elapsed().as_secs_f64()); // // Ok(()) // })(); } #[bench] fn preparse_precompile_eval_closure_1000x(b:&mut Bencher) { memshift!(); let mut slab = Slab::new(); let mut ns = evalcb; let instr = match Parser::new().parse_noclear(EXPR, &mut slab.ps) { Ok(expr_i) => expr_i.from(&slab.ps).compile(&slab.ps, &mut slab.cs), Err(_) => return, }; b.iter(|| { let _ = (|| -> Result<(),fasteval::Error> { let (instr_ref, slab_ref, ns_mut) = (&instr, &slab, &mut ns); // Localize (doesn't help much) for _ in 0..1000 { black_box( eval_compiled_ref!(instr_ref, slab_ref, ns_mut)); } Ok(()) })(); }); } //// Commented until we bring CachedLayeredNamespace back. // #[bench] // fn preparse_precompile_nsbubble_eval_1000x(b:&mut Bencher) { // memshift!(); // // let mut slab = Slab::new(); // let mut ns = CachedLayeredNamespace::new(evalcb); // let instr = match Parser::new().parse_noclear(EXPR, &mut slab.ps) { // Ok(expr_i) => expr_i.from(&slab.ps).compile(&slab.ps, &mut slab.cs), // Err(_) => return, // }; // // b.iter(|| { // let _ = (|| -> Result<(),fasteval::Error> { // for _ in 0..1000 { // let mut bub = Bubble::new(&mut ns); bub.push(); // black_box( eval_compiled_ref!(&instr, &slab, &mut bub) ); // } // Ok(()) // })(); // }); // } #[bench] #[cfg(feature="unsafe-vars")] fn preparse_precompile_eval_unsafe_1000x(b:&mut Bencher) { memshift!(); let mut slab = Slab::new(); let x = 1.0; let y = 2.0; let z = 3.0; let foo = 0.0; let bar = 0.0; unsafe { slab.ps.add_unsafe_var("x".to_string(), &x); slab.ps.add_unsafe_var("y".to_string(), &y); slab.ps.add_unsafe_var("z".to_string(), &z); slab.ps.add_unsafe_var("foo".to_string(), &foo); slab.ps.add_unsafe_var("bar".to_string(), &bar); } let mut ns = EmptyNamespace; let instr = Parser::new().parse_noclear(EXPR, &mut slab.ps).unwrap().from(&slab.ps).compile(&slab.ps, &mut slab.cs); b.iter(|| { (|| -> Result<(),fasteval::Error> { for _ in 0..1000 { black_box(eval_compiled_ref!(&instr, &slab, &mut ns)); } Ok(()) })().unwrap(); }); } // #[bench] // #[cfg(feature="unsafe-vars")] // #[allow(non_snake_case)] // fn preparse_precompile_eval_unsafe_100B(_:&mut Bencher) { // let _ = (|| -> Result<(),fasteval::Error> { // let mut slab = Slab::new(); // let x = 1.0; // let y = 2.0; // let z = 3.0; // let foo = 0.0; // let bar = 0.0; // unsafe { // slab.ps.add_unsafe_var("x".to_string(), &x); // slab.ps.add_unsafe_var("y".to_string(), &y); // slab.ps.add_unsafe_var("z".to_string(), &z); // slab.ps.add_unsafe_var("foo".to_string(), &foo); // slab.ps.add_unsafe_var("bar".to_string(), &bar); // } // // let mut ns = EmptyNamespace; // let instr = Parser::new().parse_noclear(EXPR, &mut slab.ps).unwrap().from(&slab.ps).compile(&slab.ps, &mut slab.cs); // eprintln!("slab: {:?} instr: {:?}", slab, instr); // // let start = std::time::Instant::now(); // //for _ in 0..100 { // for _ in 0..1_000_000_000 { // black_box(eval_compiled_ref!(&instr, &slab, &mut ns)); // } // //} // eprintln!("bench time: {}", start.elapsed().as_secs_f64()); // // Ok(()) // })(); // } // #[bench] // #[allow(non_snake_case)] // fn preparse_compile_100M(_:&mut Bencher) { // let mut slab = Slab::new(); // let expr_ref = Parser::new().parse_noclear(EXPR, &mut slab.ps).unwrap().from(&slab.ps); // // // let start = std::time::Instant::now(); // for _ in 0..100 { // for _ in 0..1_000_000 { // slab.cs.clear(); // black_box( expr_ref.compile(&slab.ps, &mut slab.cs) ); // } // } // eprintln!("bench time: {}", start.elapsed().as_secs_f64()); // } fasteval-0.2.4/benches/results/20191225/fasteval-compiled-unsafe.png010066400017500001750000000371531360145767400231740ustar0000000000000000PNG  IHDROEbKGD pHYs  tIME /KSe IDATxw@SWf!-Y((AADAQ܊XjV}jGXG:TD@E",Af;@~y#{(/g__Y/1BWW+񢢢RSSKKKwpp|_SS###=_O?aæL 8p&}jff&''ַ'ODDDL4аoݼ4dnnNVTTĸKIuPիFFF؉iѣ|RKKk„ ]G:u^[[{РA222III=1cZ@77777nRggft÷bo}MR4oe͑jmm-Nׯ5iA;vlBBӧ 8ҢLfW>!++믿裏(:~xll,U+ǤIq&::ۻL HII]gmmmqq䔜|`CC۹s'Oۺu͛7ccc++++wbbbtttaa!gXVVVUw͔r&ApÃZvZ--/vx’+**L&=zt>}Z*KBdTBTTTϟ h\nXXXjjjee%466vwwo??/_FFFyxx <8&&&22LMMmC oﴴfΜy嬬}}ۗ_%$$lذb5-/233kkkUTT,--=<SBHvvvxxxNNNUU211TSSk8'!O?d }Jy:}}Sjkk)'O/jZ'"##555<'N߸qxžpBLLx￷z7mڤ3a„SN}JJJhͽyfNNNee"qwwk}}億*uuÇ6Lj+ZDDիW !nݺuرco޼Y__OYn{ADDÇKJJdddtuunkkK?pǎjjjJJJk֬!|H*-'fΜIy˗L=!$66FFFG{eLLLzzU ///wrr֮w^@@=%//Oܻwommaôx<^LLŋ_IEEE999RTT\ziYYٞ={ꜝutth۷ox{{\tԩS'O W^=uꔎ!DJJ>|ͭ8((+VhLLFFƁܔ޽ӕ+W}Ϗ9d2Gzȑ7mffG.H)8tP☘/RlÇ ttt]N<7o޼˗/=zАb\t| m6+M/^hv%iϺaaae322ğN,L]]ݙ3gccc#++z~wbx<ݻwW\ʁTMMkuu.^(##6`Hdmmmgg7gڂ"?LII\]]>|x .KhѕgȐ!Tfff{x7FhTBGѴg0K,}yAIIfAAGA_ڵkם;wLMM555鸶x# x˗/W2dΝW\ϛ-;;baaў 7o{+W,_BKKK׬YCxK.ŭ[n$ ǎ{)M LBDDD^sαX?? 'rn޼),X`llL:t?tRHHH4hݷ6!?qqqoior 411yYRR=߷o/^ܹs'''ᴧڔF133ܺA322NNN@'==`ЯhyyyF3fL{ʟ$f̘!^mllΞ=V҂tM۱cǍ7hZijjҦԤM7a!$$$<}tҤI@NNN[XXСCiZ$&&.^T`扉iiivvvؕWiAA222H#ޭ cbb߂WrUUU:n߹s$222,,,***GʶfȐ!QQQO<477p8m^謆FFF˕ h$*MhQG$҉V.?511Yredd'ORRR!:::'NG@ɇVUUեU ?nܸ= h ~xӫ>~nϔ B홖N|VQtv^}s=zD"!!AAAA|888֭[g̙l6[FF˗gΜyÛmNigE=XiѴ'ޯ_?:hFO]Hhg%0ڤbŊ7nܸqCYYťw!FЉ Jc0 ,hdB4!$  g i٫wZ=}iӦ_?>|xժU ;Sr $''O4Mqq1͖N{FUH}?SZZC?--,͉F.j]!//߿CPlhhb~~~p}[+ztT[[}Ƞ Éohh'/^KRTT3f̘1cʞ>}zmMڳfo К>}yCStuЁNOyF5YYن>F Ы/+֮]Zrtʔ)җ/_KKK\ =8p`eeeRRx/^䘙uhX)3 BP V66mm\ɫ03226oLo#%%ellr3333HL׏d<~XUU^}$YJ=6ۋje3gzE=fڳn<{LGGPkkk+**>~X|_~ph%wxrYYYڴ}К6".zUBDD+BǷmִִ/n#QG=x𠑑n}}/ Y,ּyy 999s sssYYْ+**52''ѣwqttʺs玥e#99<ᔗ߽{5̟?ĉODD\IIIzzH$;vxlw̘1'O1bNIIIddĉxwcc㒒99iӦ8p...****!#G8t萣#NIIoO]txaedd|ȑ#C겲ONN>w˗/ݻ7{>}4..N¡Vvᥥ&&&ڬ6 [={ґP{ D>MLL;;;ƞ={VCCAWWjjjMBYYyׯ_ɿhdd4jԨ,--׬Ys$KUUU FA,_<44UUU Ш%~~~.]|rCCW{lٲׯӋmX,֐!CŗƍL&{׮]mvL B+9 E͜9yq\\ܹsMLL<<<ï\ʯ(++>}ifEPII)55UFڳn2Z$%%ihhH=vM壣###kkk =Fdaaaׯ_И9s{رco ꚝkcc4o޼ .|mB[ <{LWWW*8xCE]7oꫯP=͛7]66FFy#00JJJ7n@UEB0<<\SS7NH E\.w̙]9 -,+++WWנyO޳gýZ:o[@piH @Z -^%*,>K+3U#-w2?QC_R`={v[~}{fff.Ǐ/^u%?ҵk׾vF!СCh]!-:cϞ=o߾}H$joob 6lI~[n-]4((믿1bݺuuuu7oo'{zz:::J9;;;[/ uVDDDTTǏ\RZZgϞлw/[Ǐv^ˣGN0!$$~rhhW_}p»w\믿FZ<<44; #G PviЃDEEmiX&?\^u8@ͷH(/\*7l駟!/_^f 8p;wnذfrŋK~׷~kaa䤬jժPss<=q$Z<%==YIIiKǂ͕=<< {zU,B*nOTBY+B%Ebcc.\X[[+++kjjJ{儐1cƜ;wrӧOB틋Ϟ=;qDBSRR222 555H9r{zzmٲe̙eeesr666˗/ 3痉!‚\)C% K,?Gqcǎ :رc6l 7nɒ%;v===Ja0o3f !_~Frvv7nСC}||2宬|˗/?ǏB MMM\ !n׮]vM`===.+//iUuh딊;6@u>Yl6eK˭lff&9%--ĄF!"555--‚Nal6;;;>宮f2L&ىhNy.rFֵv*0ӑFEiD Q<OYYF3A'6zc~~~^FiүCsu bccˊlmڽ'iaaaSSS@yիCCCũ󍍍QOOkұvvvE;;;T3t;[nMMaÆm߾ĉ|>q̘1aaauuu[l6mŅ LѣZfff{Ν;̝;?7n\yy?\VVfdd4wE]zGEZte7UUҡC/_?n+P(z<ٳgǍ'|}}N'HII ]]ݽ{O>ݳ9rRII !?8qgfc55FXRRBCjhh#-zS^t)88xɄmm#F<O>& XIDATܹ/_orAYY8$u,!"eee)iii?SחiI//\reĉoiӦE_o._JN!F裏kjjݻ7w\MMͦ@77nܒ%Ko`߿+ @O0z[U濱NݡCoѺ~ (h{Q^^d2L]]]:188\YYÃzuZ=|&lΜ9\.Ɔުސw Z'jooOoûa===>///}+V 8ի4 ^fl6 Ҵ f͚?~nnmf͚S]]d20J4'DIII>|>8q۷$;<O|Oy|߄'66֨_^VV^UUUNNNiCCehh8|q7awLH~mggWoC5Cg{~njB yш#ƌVWWe˖iӦɡ9zoBUUٳk֬1119w'O~'yyyGE[ xzzzzz68jԨT4!@7?iH @Z -iH @Z -iH @Z@Z -i z~V95䇙Kз^zU,D9L@Z@{@QUYۜSDyRa#Pi@Sow{"z^oj  3iH  -iH @ZH @Zǣ>|T(Ң~aѢE۷OCBHPPӥG7nܗ_~I9vؾ}P[o'OD -h!33 rՔ___uuC1u /[[[B immrJT+{KgKJJ OQ[O?߿#n߾|r)֭:ujDDDiiڦMlllPUxر3gPRRz*'X,=N+++{xxH@Jm۶9Jxܵkי3gS̙rmll0Fui$ȑ#zzzoFZjxJhh=a===>///q}  QA_KYXXl6fggg9zbbׯ1cOlgޯWuu5?e2hN_%'wC$;<OYYy'|zQ廋^u8@9M.WSiqЧ_DZ4;:jԨT4{rnaA~{d=طKitP(G!D]]] N+R3w--dɒ_x1N - `0SNׯ.PWW߽{w@@!D"_r5==2BHff7\2$$DUUbiiyٳg?}ڛEEEmiX&?\-OmmmO]QA*Qci}jQQQ@@3괗+?s~5-Mfmm㣨:@{ݽ{wQQ.j}fM6dߟGEENX`ù\.&|G9ӥ5kֈ(-{iJJ"}Wl۶ѱx111F -^1a„ѣGN?]IOO;wg}VTTtJ)-x"!dٲeaaaO33fu + FXXؐ!ChO[44vؤ={B;)t钞 !v-g2IΜ9i!''zΝ?CGGgܹt<p\MM͢"4!@OO/Λ7J]](==̙3Grx}lݺNSS B ܼy377W $%%B-Z XbKuuéSЖ=4-f̘faa!%ݓ//@HLL x/uiGohho'''ɓ'X,nw#-bcc555%'"-pB)ׯG -^u֌ PHںu+=ӥsDqvv)_uΜ9>>>7n@o!522HT^^:@ׯ߿aa:iyM8b}%%%>>>SEcL&ޞ񴴴PHٶmۦM$oU\\j@Zϑ#G233C.]p[aׯ1c]PH믥/9-rA{H @Z@Am)mlpܰ(`ESow{B yž](`荄9?lX -iH @Z -iH @Z@Z -iH  -iH @ZH @ZEihhXvTqq1iѼ3f(**JI{hƍ7n܈&@ZB=^.۝UѽBޝuO___OOtEguS캳*x!U.){i@a$ ߴ())a2L&S 2̂4'Dihh־|_9vtw.{/ǰoh@)~G=Eo36 {H @Z -iH @Z@Z -iH  -EppGAAK|>`0̙3і1-̙rmll/_.*ԬgΜA[92=d 6|yyy*ch?޷HKKl6ΖyTWWiii=:55m Ӣd2JS5a„_577iƌhK7~??Xm۶eggЧ377o:gCCRFF<1@ozxC{y KKP~K&Aii!BP(Oiax-zHԘ1cl2m4999w!BǏ===333֭[455ќ.-X,ɓ'.]{nڥf+Vpqq־SЖoN=o=D`` mCZ5:^UU\a#;vXhQ/7ݻw4_Ʀ\={hʔ)Ǐǚ{Ƞ ݻgyB<}YCCCZG;vW\ ii/_󝝝sZ* o񞨫裏LMM9oMMM||#!_gϞ=h //+W7رc;wΞ={ڴi#FhtwAo])allV\lmm}-BHiiE~6oL3gA)((`0aaa4L4iݺu!!!* .к:ze߾}GCٵklkk^RRRejjjbbg! c͚5FFFUQQ1w\ u'Ovww𡭭9sM_[[pFqwCBBgff㏟| }U|/*.sʿy-[vGDeee3g422FjDx/W={]( 5kDEE=xT$ݻWOO644.\P$EDDXZZD_~EMMP$\rѢE"f竪>zH$رcԩ])a\\ D/^477DK,D<2(( ,Kw߉D_uʕ͖gڴi-Ϗ?8w\xܹ;w,))a2"oɒ%"hϞ=ZZZti^L&ŋqqq222DGK/W\9gPX\\p_c;V$%&&*)):uJ$~߾}.../^022鴆}}HoIIID"qvH$,st~Ar-[b Ht=UU՚-ɓ;fC7[oo ٿ?MNN ۱c%_>|43f !TWpqq"L0!&&FPGGGkkkBȒ%K._,RHz^//gϞ_zuS2DFF.]4::5zf`0:Zoo @pUooouu¾}BFA{ C ~~p233 !s%̚5.xgٲe CCCcƌ/^|+ ޒ#""f̘!##?nܸ.~EZZZUU].\( [i;Joʕ+#888dddHޖlfpޢ;8;;޽{ǎ3gΤ? SRR_MMM7rwFFFr8TYYHWWӅTUUo(((pB 6`bbR[[rܹe˖Y ܻw? lZB:o߾wܩӧP(ܾ};rFFFtΦcdРt$kr222>&55FgnJJJc##vTZZ*nA&IWf5+.sKHaasm'Nq[4" -Ì3f̘QVV0bĈv>r{F]]]wwx UZZ* ϯ.**211!B\]]Ųx񢁁JK塃K.1<} "##UUU?~ȑ>k׮]p!!D|NYY]F5ھi9l6ǣ|ӨL|ʤvD"ĖG򽕕en\aRRR8Nӛ͆n޺HT3ٳqFzаQ?N9sdxxxܹs'--?>w!ٳ PWW8qiP;wˋ{nzVsذav=z(׃KJJ8*=r+k?~YYYytVVVSBȤID"Q}}իccc_Wx</ /^\teff&''wUBȾ}hG ))RSSC_j~{]F hzQ/+++j >trrji<ϯ$޷oߘKK-[߿uֲ2 ''O?tԨQ4-޽KO899ݹsN屰 >>>EEEӧOߺukVV}͛Lbmm]TT|Exі=]:M6Ԙ^CӧW8ŋΝN88tRcczBȔ)SuT[ۛkDxn'5ڽ{wRRҁ{{{ӞtP6nX]]?OVUU}! z&'\w'P.]ׯ_uuuee睜P'^gڵkSLAzD@0킴 -iH @ZH @Zd!hЌ{IENDB`fasteval-0.2.4/benches/results/20191225/fasteval-compiled.png010066400017500001750000000534601360145753700217120ustar0000000000000000PNG  IHDRUOtfbKGD pHYs  tIME -#ȧ IDATxw\SWf )C"(QAAJֽWZj}hK-uօZ  (C@d( ;!_}}޽{{pܸq믿~ VĖaU(++kƍw;.ܿ?%%QNNNMMmĈÇfmBBB=zg555ׯ_ `0LLLz|#KKK_nr:Ěwww1kرcoܸz󁊋;VQQ!//ojj`0̙Slرcǎ76lXY^^{V ""B_p.^8p@>-Aξu떛BȄ ϟ?F `>n8qqqe˖-''AԻ744ܽ{vڴimŸ\Xo>[ׯf 11c^t)))i?-&&b9;;?^]RRϏbIIIR>*++###333kkkt<|ŋ/~;w=<<p'::ٳg>3go.++Ku MHHpuu okkKEWxǎ>>>Ν+--ݶmh>-7oDGG~VVV]KK'''թ8;;;99 箸OKHH 0ںAp8-[h4uSϞ=/,,x***Ç3f 7}+++1 ˑ#G!χxظq|v޼y,+00ESSݻZx!!o}ĴilkΝ;͛ɓ'x7n;wNSSSGGGǏkii}l6;&&&44TLL޾5_ի:h"###BHNNtq233?Z[[[>Y3B9޼y+((%$$dffnذAܜ3L  >ȯ^:zرc ߿ŋ5kִ m*((6lػ:x?#''gĈnnnuuu=:{lUU՘1cLMMrrrͩү^,!;;2xάDSSӅ hee%)){{˗/tVVV׮]k---=qD*B^(6QRSSYҥKW##'Nt#geeQCNqqqZ駈{{{W^QTh[tⅅnnn-ZKjj*˝:u`CBB޾}Nillicco߾(*$''xWp5oiooz޽K:t(5FUUUs̡`gg'// 333*<{lѢE-411yYVV ~{G=|~jjСCZZZzzzyyyuuu# s̨K333߼ %##chhbYgϞihh(** &..YWW'++L;AAAm/YdРAԤ^ITnϷv|<ʏ5ޝ/rQQQiiáN" 8PZZ:##ڛWiiikk謬,KKK A򤢎KBBB^^Iuuueee;4 ],/?˗/oU"z, !ޒ͛v <㙛 /,>>>##̌zk֗QA { |~=kjjtttZ\?W^^.?iu m?'@]'SYYNfl6o,jnnnnnn֧Ҳͷ4Z[[gddR9G7 ?...Bx<ÇR'߮r%%%bbb- nɽ{=ʯ^255)++N KJJv#FGDD ^^^UQQ[\ɻ8::?ťK?NdaaaӧOWTTxm{E eccC]/SAlBu6ڻERR SSӻwRG{ B122.+Lj$W_?{,*****JNNnرFfI}jljjhovMM6M;jPPSS+...++ksYQQvZƵS|_-zߔR;Z9+̨ 뚚xbAjhh-* wCCûfEx022{5s/ {z~nMVVvǏgX/^}Mmmmq HKKWWW8 -eeNy~GE-j.]gϞByyy'Q8} H8E0Ueee[6Aqq޾}*>|ps"t[uuuss&F/N_uS;/x-Ƒ6ssssRPrlcccꄴPRRrpp&6Ʈ%-QgT0JΟlnnLDtDwruuUSSKMM jhhpBJJ !Æ MMM)((x{ڃ{n0QAKbaa!&&#cǎٯj ڙ@}$%%,XpĉG=|РA /_lll4771csgϞ=zfyy;w'Mŵp8G6lÉx#==֭[UUU߯LIx/~EbbzmRVVhɊ***ԮvWL2ѣǎ5j۷o:jZ/_ZXXÇwmll411a߯6m}u¨Trͩ͒ 6KKK'Oxѣ'111??„ m=fse[[[EE &oNNNRRR/_LJJ266oZӧ_722KQPPXjUDDÇdddLLL<<<:9Z;>{EEEթN>3{rrrVuVZZǏeeeܨH!C 9sfTTԝ;wtСC}||8IJ5l0y֏?&;7!dW^MIIILL՝={GLL۹sM0!66666ήhʕnݢf`1b{Oe0-&IJJJLL՝?>5ρ`!t:Ap[$* naĈw޽sNCCδiGZΛ9sfdd[R0%F3gN\\\bbbhh3AtuuOMM}捕;vj||͛7| \]]۟|r@Tт/^JNN޺u+vz7oΝ;WI=|ŋ)[GCrqqa0QQQ(Ex111jjjM|ZRRRӦM{OB]QYY9}o %Cuuue2o޼A5˗/cbbu@Wx/@@@@@@@x(@R@Kxt`HcM@F6F>>Ԓ#""\\\6mڴ`Yf?<<k}qk}So{aCɍoȤ0_T:  ]F!M'ע<""O֙ ]5Yt1$MQ(>ЁQtW"PJJʢE=ŋpmq@}[a~] edd1/??z] ˗/uww߼y3!dӦM ,5k&$$:uۄ??ࠢ"..^PPpܹ˗/߿f#@+(((((",^ѣ+WL2DHHH8q'!RYY9>>d2}}Ű7!DKKKVV2걤$!$<<x<,KQQzT^^N׿ue{{{OԳrrr111.=LMM㉉5553kFDD WTT M0aرJJJT :ttttuuCBB!'Nضm[UUr<Oꝑ#G2LSQQN5޽{"VA>| KKK&gϞ!CxyyQtgΜ2rYfPBLRVV6~x)fffL&SExjuSYY QWW𐗗~LMM<%JKK===-,,-... \pڰ0999b~PVV^|9uUVw_,X zMz̘1VVVsdX;w>}:aXf:tPeeժU!QQQAAA>KII jؠ7cY,i9RN%~w spp0aնםFѮ^7nP-%%%III111~~~;ww^qq!$ @KKҥKK.500 lذSNmZWWW|גQ:SE>H$#''gll[AKVV?jjjYRRbjOUTTTTTrwwkz^d ߢ]]6~6f킏lIt:]#NnuކF!VI~>>0ܒRUU%''f[4 l"hlEEE:CAAAW+B1o'O@]dѓ'OXU(kkkkiia Ay_f222u֩DDDKcc_xA5o>X諆 mlP:AP>?F wG8MMMi޽̙3ǏOOOljjڹs)S;WYYy3fZ:OJǍFөsE yygϮXBEE%77&Yym?0觞`m IDAT^\\\F77k׮]vm~3 @zfY;5q3Nuo7^a~sUx<  LLLZ__a'''__߼}yV\ Rh47F/_'y󦠠ˋxsQVV&Ȅ;99痕)((P}$%% m4z0lذ#G<|jjjMMM/_DQɓ 444HJJ4ꆄBN8m۶V\ ;j(111MM}hÇ_~׮] _KMMرcǎϚ1~[.bFxkw/^&oB!y4eWq(dK y9oZ*讴9Z%j8uYv:0$e+P? x)j~vuaf8J 䧤EusH<zFnl}NOѣ/^ؿ-l;fRRZg&qP ~(u%'(xUmGPۜko9y~3 `Tes rرӧچ__W]]!$;;{ʔ)F|!ӓdR &O2@du"Z2 8qq .>}zцSN׿lٲٳgϚ5+))i޼yC)Ay(8wwwB !%%%_|!ZSSɓ'&MrQQQ>>>ϟ?'8::LepDFVV"..N= qܲF{{{f;::jii=~ 4HGG:t(66RUU}:.8i$BիW®\xҥ#| @600 +//ػw/YtiMMMhh(!r, @)xC2uСӧO;:::880 BȠAx<ހ!'O.//wvv^p͛߼yg8"M[JMM899ݼyz,mРA.]j(cUU7n~LNN `[38m^rJͷc-Qh }ft&#KpO0čw&Dlb*P?CɍoV I?ޚ0F"o ҹ%ywç/]?Z@44nlMz™3g/^(h 311(.. ee˗s\BHvv 077OHHw6,oC@8p…C ZX,֬Y:TYYieej*BHTTTPPÇRRR!sdX;w>}: ^nnn򂖈[[[IIɀׯ766^tiҥ 6l )))IJJڼyƽ{0?5Fn255+***** 7ffffeeJHH7bz:.N 7n4b:5XUU%''`0"hlBl -}yfBȦM,Xp5kP-L&qqqGq@] ]G*try- GTPPPPPEYxܹs !W\"899}W###'O,#####'++]f|deee cIIIIIIBHxx9x////Ajjj<OLL)??`]6""ĤήErqaa(_DnHH!ĉ۶mb0zzz\.7886xܸq/_aXSL v'NXZZ2={ 2kرcƌqss={ȑ#E>8 @1L@cuS11믿'jRTT$<}b>!2WZܢ]L]Sf9JC~ᇧOݻ700P^^~۷oGYcsZg(o(`>)Wyb6<t3$ݝj%k:uw9zhrr2۷oݻwutt,,,?ZsrjP#Sen&cI,򏄄 `llLi186?O> :gvy3!L&SUU5ީK_|5ީK ~7gϞEA@uOk͹(+(ŋ !7nȘ9sJqq9zh@`NXښ2wDF5zzzZXXYe%_zԥ|a&n6 %nӥˋ{JLiiiuu5j ¸n#BҹQN]`ٲefffGVVVfXo^jj !OsPjj$@;lٲ/..BYYyǎVVV)@W>,n߾mll}/_߿?ٳg̘`0>s)))Q]]/\zusy7n?ɓ>b]<&L>}x%WxaZ]OEEEsn߾Vh C;qıcn޼%]vB.\3g666B~-66vŊL&T2dȩS>sGG@BשDDDxxxBnݺ6jԨ3gRϞ=ȑ#jkk} ,+,,$:^xeۼ@fС/^ P㦦UV988ڮXf/Xl̘1+V_{&?~<77L9rJN8Q(..^PPpܹ˗/߿fRφ\СCw5jԖ-[!?CPPPyyNjnn...?YZZ8p"))beeeof---[djjj\\M!""$!!ϟ?߷oTllkkk333{ hiia#弽 !ZZZ'Orqqq#F2d!/r:::K.]jUttLhooJqww1!Fy{{ CILL ??6443gΦMF檪geeEEE555}vvv-: ><11g}uėBmtJ}(ɖ[F}c'q >d޿eee<3f (//'̘1#!!aҤI222UUUceeX^^N$^FFFFFѣG%$$OU>leea6ݢw֥6o,..$܈S:ﶧKf=v횑ɓ !G>vXn6mgϞ=`BHe?FgX! ^l!D\\D񩪪Ztɓ'M>uTVV- =IJJVWWwѣGׯ_4i!dя=z!$))o!رc…[n^|Bgֶl/TTTB A- QǾ}|ǓhnnLRB\vY֯_ gyyy-Y$88nJJJzzz:::uuy…6l4i۰aC@@… ]]]njggf͚w-299ͧlmmGٴiӦMNZRRrL3;;;{{ٳg+((R$%%Y[[Xqtϝ;geeUZZ`+W]@OYnݺu[RSS|1f/d2II۷oB=*//e :TBBٳgGNHH~Vp/:#Ν$''tj?gϞ}葂͛Z=o hSה-af@Ȉ xžh4W_}ua~ׯ_ޥr%cU?ii1yEw qye-wwh J;c|>/ yN9 A򌮼Ů]Ο??3̰0//@&3gK,YhjiiѣGu#S|j,joyeCN&_JYJed֭ ˢE:ߙF:u#ĉ4N gIII>|+(i3PX|.QQQwBHcc#ϧ)N]:sM---E֎@M;K4߷ll%o>ZYfMxx!dnZz5j ݉oRu|SWWggg'ں37;a]qݺu_~Riiₚ@ԥ?Gp8SL=rHzssƍ?˩egg0 ss7RRR__|oQuSʊ#DEE=|0///%%ܹs===Y,Ν;Op0?ﭰpʔ)t:̌zn[۷o߾]ҥKK.500PPPذaCHHHIIIRR͛%%%444ݻFs"{}Qo}.]3|'OZXXB%K<}{VںEKVV;4333++PBBBյ.7m_\\/6 _|IgͧJ~~7`鵵-̉=i VݚRRR? YY0 C[_XTTTXX(zkO>>''gܸq>>>6m ֞7oޑ#GVZ|||#""ƍyMM03gΜ7o,Ywޯ:$$1mRSS(**X2EFFߞFxg…+W,**0`/\ 뭪Aҵk׮]VEOO/..?X,!ZEEEJJ\T]]NLϽ{l˗sssK.YZZ秦xAmذ|qqq9b%%%>r@(9"nnn%{7o^mmѣ/^oӥ8a„@Bȷ~kee  'yCdExo 3s>)\[}KBȢE}}}}}}Q.k׮iii9;;BzvkWۜWZf^2pm=\]]ݻhjjΞ=[II 5y{ `DTjs;d$?W\3gСCUTTJKK/\0n8-#:0tPǧO.X 11e^KG͚2bĈ:D0={Bx<ӧ544PS蝺tÇ?ŋTTT?5?)))999%%%FFF4 5?\.7::͛7\.7555&&pBD-L:5++TL!W^={L8fr?+*l.Buwqtt2~x>_RR'׻vz#ڵ tRR>@\\v5EIDATNNN~~~T 9sftttll,̘l77EUTTlذ!,,ӫWf21117n<}͛MFijj:wNo*KS̚5ϛ7/??ܹW^г&NH6lXbbɓccc\nddo9F366f0qqqASNMJJ&jhhBLMM !""000#/fЃrrr,kÇg28p~E\b$LLL喕)++S-t:]FFZu !dyyyݛ={6͕ \BϯVSS{ 6UYYI=RQQ!]v-,,l\MM <6f[w+++t Fmbb^^.埭[ԸL4*./Blll!ޏ=~I\__nj㜜Bș3gG~IHHlڴ)::2dȐ^^.6gΜI&]Ancǎ $())9;;WTTPS(((ڞ>}˵?o<?.JJJGٵkײe˔[?:rmmm !UUUmn|cƌx9AjjQLJ/N`oͭUKg;v`0bb= @[B ###*** WK㹹ZZZ> QtȈkǝvvuQ0χ000@r ?-ZnݺuV>ۺuԩSs1 6o,..$܈@JJJ",>g\c^Y9@h J;D (;tC+lD}DGq]OOw+--dgg0 ss 9(3fpܹs===Y,Ν;Op0?}FzI-%%%III7oиwFϓ366n2446uT__O?Z:@$!`TWW ~kݭP>U>ytK ļ]{ ƨ#C_y>]!V)=ikkkii!u tSSׯ_lBHZZںuZw9gmllˎ:CtBB_5ߡ*Lo!0F\x]+_J:(`wQG0F| c?GWojjjNNN{p8gΜilltpp yt:}ܸqiiit:;888**JIIiϞ=/^̅H??GN0  9(_{wTUpwEaTE \@pAMEfMBg҆,&3ktJDDA$AԦt^E LDC @ {?;wxAY;BV}⟯hnXmE;e GnnR5IGIx?֍eć kߦ0?@?@t޽(--|=~ǟ:uɉd=\3eʔt### pi C!JKKKJJ?ۦͭ/ho B8Ð rDs$j9266sNwQg+#uVVVot-mn9rdA\koxn;_2rO|Q{Q# @]9ĉaaa'@{Yx *=\_V-X?DrVo9x$ZȄeRzC`>Vv7?@?@?t) -%%?z ?@?@?x~!C<ޱx255uV5]xxMMMv}~Zd]CN:wdC QQQ?Ch¿dktΝ;jBׯ[YYn0RL 2? ϷS*uuucƌBl޼y…aaa~~~9swܨQ윝O8!-""GW\~~~vvvwޡCZYY7]555Cuvv޲e D555s9s挏?677T*UIIɽ{ÕJԩS׬Y`q7n򲳳  ܾ}{^^U,IoRJ Тʄ;vpppPT&M***jb] JlܸtDO>5jTJJRZnaÖ.]*xʒ% j7>w$I~ZnjjrHXXB}^;s";;{ԩ&EP1BR)Iŋ!!!rÅ&M~BPhǮ_>B(J7n룢 ?K.U(AAA Q( B&+++((H__j̙SdeeEK/=hYf={wcƌk*++K.Ç233ryXӕ>ڧO33+W=<ǽSNy{{[XXk1-22DOO/***==0'=CBBo544+/蚙NjӧOqׯ‚hwS_O>j\^iffVQQakk{=ZvXFs٭[ܟ!'1̛7/33sȐ!wHJytիWWTTp@[~L%##AIi~Q;111NNNFFF .<anݺ6EͿFVmmmUUU-j*--D-ebbR]]-/N!wr_B===I555ªU999]ݱUUUrYYY۷/==Ӧ۷ooh>~޺uo߾st pqqXlYTTԟhJ ,--uuuu Y<==KJJ^xVFGGKT꫑2ׯ?~ƍ7/^9c gggA˗'%%999mڴ)>>>99СCǎ? ZQQ1tuݼy޻?Ł~~~Ç_dI x${Gtuu8qܹs ={yFR8 ` <9=999::@ /_/ſ 88X{_j7֬YS[[KL:Q@@@PPPDDtɕ+W6 EpAhFffcmmݻw8TTT^|Yc*]?@!{==b<͚5Kь1bÆ ]Ͽx @B@W/HII!" T#_IENDB`fasteval-0.2.4/benches/results/20191225/fasteval-interp-unsafe.png010066400017500001750000000375411360145777000226770ustar0000000000000000PNG  IHDROEbKGD pHYs  tIME 0] IDATxg@ԥ"HDAQ vJPc7XbШ`DboHATAE:cY%*Z3;3;.ϏtIナ [H @Z -i -iH @Z_@Z~w˖- sŕlܸaoݺPkuLƍ߾}yEDD|8?8uƍKJJ`ƍیk׮X[[=?G]^^^gIMM횗ثYl{]ɘ2굨GOƺXMMM___HHc@۷o4xg?͛7JJJF` iѣlܹsgΜְ&,,SFF[KK˥KlkkIbbbk6999oOZxxxTWW577Fuqp\6۹l6ɔss`H߯utth+GA?GZ[[ !gee%LJ޽Ν;ɵrrr4ɓ'/))ijj266vrr[]]}Νϟt'''j$&&&,,M7nܨaGٳot&9~p/]SSSsݲ2 ccI&[nB޽{݉'?먨Fiii'''iii=>ˏ1bرԼ7n!IIIiii9;;SKpsv)&&FU&u}Ν'OTTT )++،1'վzO:I  ,_\BBG1coٹsgXXXZZZssJԦM?^u$:2***++Nkjj:::Rծ]ׯ_ϛ%%%/644ۜ;wn#Gnz~{ZZڶmۤ=:;A߼y3--N^^ښdnsΝBȖ-[h4ھ}}qyyéիW.]_矣F>}:!>555..nԩ8MAiA ;wNLLÃrΝ7otsssBHrr544Ə/""͛[F555;vʊ`?zĉ>>>:::Ce0gΜQPPpqq%9rZIIǎꫯ{PSS3qDII755͞=Ą円2̌`B^|yI999II‡fff]:ȩ !?`B>>ZZZzzz ԸPmmmII˗/yiC ]Guƍ{{k׮ YXXt{m""" ϟO ߞ?>j({{Ǐ;w$''X1c$%%333_VZGRᇿJTtR0xतrEEbYYY;;;ƇJHHQTTTTT$PDDDTWWZw9rȃ^'kK]6m{ t.H"o/_Zz5u5mffoĸ΀555>>>S_fܹJ݃,,,7n܄ x=EQ%RRR񙙙FFF&&&4B^~6yd^X^~=%%ƦW^ѣGOG뺺ڟ444󦦦ubj\iiigggB֋/222TUUkOmmׯ_'$$3̞w]]&[hzᐐUV'''zؘ SSӗ/_뫩IIIuۈ6}׺>:iT8p ::J# Ҩ3>>ѣGF-..Nׯ_4MWW755JVVg/ ]s\#FP˩rGIII@@e=y`ȼuuu=_#F6hMMM,***--oii-tϟ?rԨQmΞQW^p,--y_-fY++_OOOOOYYYohhpA555 ;Lu~xû}V1OIIBԲ.\zggruFII@?{^]=$##{-""a =.Pgvo6664M+))Xr ^3Lb#=l6MߵnQ JGfv0Ϩ XZPLiPm*޽֮]SJYYyʔ)qٳ@YYYgggŧ855I&|oP3336/ukPJ#e*++wxv]&!###$$͛OUmp V?O麤ZEi6L=,%+++++K*-3z؈z43ԭ6G4mşn&8MiAUUUwwjpzH ƌ3݋G[`B/^̻Pb]\S{?ͦ? j}X(~g6˒Z[[edd|||x'^m@P\۟:L-EWOO޽{,"dRS999:::eSoQv?aa8Wg͚fMի.=/++kY^ݱ-*2' myx.:\VkkkgwjjjZ[[Q8NiiiA.NCRGRRYĊ{T=Prڂ^z⅊ u˫ݴC#8Lf:llfCCCO@O)-FI߿>|0))INNnȐ!=8pï⡮¤xw,Åj055e=СCO*]0`^|sθߪpJII!]>3d$ݻ;vཏoݨN0qIIIrr2ٜ6{<455R?CHSC~:??_WWKf2/_=~fEOW=:#^UVVX,A3G*P{Nll,o'((hϞ={imP A5 |#Q;sL@@u%%%RRR-za2o߾.//OLL755v3gXXX&$$XZZ Xӳgn߾]]]d2kjj>|X__~FKKK6lɓ'nj#//O=.--MmELSsrr?~EL81,,ѣ"""/^HMM=.//_\\.!!1f##gϞ]|YGG͛7=;wn@@@fffJJq_O544<|z֬Y]SQQ>?/^'Lٳ`;;;ee8QQ)S׏nnn楅XRR,+֓Fn38011iiigټw{m>+W dnn>lذ̓'O>%%%%//oĉ]9\.ŋ mO2-!ׯ{ǏƏ?f̘^ S3VSSSi[[ۆ/*((888XXX?>,,LNN}ZHJJZO>?544ƍׇ.F-&NH}bذa+W}vBBIڑ#G:::vL 8qbxxxaa)S:KqppPPPrrrr'NWpuutR||1c<<<_pB---'']]]))6Ӧx☘Ԕ!!!uuu(СC333kjj>?DVVV!HKKZ*22211NLLLOOɉ8NOOrw2h4&ٳ6gnyq8ټy󢢢n߾]__{RQ`ooQPP`jj*!!hѢؔW +++ϙ3-),,y@>Ϛ5 1ܹs'<<ˋCmذ#Q?~ oѨ<55 Q1 -&""2k֬ùpႌ ji0pۇTEEEX#^t-G -iH @Z]B]|K(UU5ln6E4~K: /nݺuѢE[nݻNOOꫯAfMu^_6maTBN:UTT/|}}ݻwرs?G677_fMkk+!֖SNeXG{+BCC !?1clܼsz犊 TgggKKK{{{HGZ|2V^mkke˖ 6DEEݽ{7666>>>===$$722Ç͋Yr%_&O~ʕ[nļy̙3;GFFJKKoڴiɒ%>\v͛6''P:.**jff'&&r\RQQ?}DDĬYΝ6rHBHFF!$,,lԩk׮BY,F诊;wfgghŋ]xȑ#{155ݿo~p!'OJLL_P9}tKK p8w~5`^u;"v6۷rMAAo*:tKKmFe`]^__JRRhݺuzzzw1W= BChHPP0###>>.99yɒ%:::T@XX2a„˗/BΟ?O177/++x)S!l6; 444 -)3Oq.]oo߾ٳcƌ=zٳgmF4iҥK&N8ydggѣGBh4{&L@1447nIF WcPn'~u[$чk ~ፈ]nr-#[\sHtQQ4-JKK,XPPPN,Z(%%dVVVիWYYY)++BO?̙3ȑ# @)8\S n$۷cǎ555/rvvڵkGKKKUU;VYYijjj*BHtt_bb"JOOp^:'4d͛7 1Hsss'''aam۶455]|yٲeL&SZZzÆ /^p^:'%ZZZBB4קJdddddd y[]z:Nv[dQTTTXX}}X*jN ӏjIIɆ6S/_*3ӛO?} 333hjj/&p^:goii9w\SS ={ܼk.wwwOO@UYYyС9st8/ZHիW̙r[ZZtAZZZ@@ ۧs%!!!))e˖۟9sbkkvZ f//ŋBϋVSM2nSj*ҼDѶ' za/N*K Z| O!%-ZH I!]}?.z*ߜ?h4r': {ꪩ?~ŋ۲p9sL4f޽aaaUUU .uaAZGuo;שSVZVsŋ&Mb͓f2iii֭[fիW/\ѷ;hѣw]bEhhӧoN144<{ٳ|}} !!!!,NNN۷o7n̘1+..zÇgdd8q믿^dɤIܨτ󯌯/H,+00HLL2 ؘbŊ#Gy{}rJ&/LIIDATÇ}~dddt`;#ʽ{!'O~166z X;vf4:N]G`yytt3z;# .h5k/]Ovȑ5ky+з/":KJ {v}QJJmٳCCC&MMMM}^Uza^MEb 􄿿?F{H @Z -i -iH @Z㲴t:]EE* ӓtrr*.. `0k6Mɱ011yMǏ766666y*?c*++MMMWZEKLLdXzyy9;;WUUڵãyEuu IddmBBB._l2&)--aÆ/n޼YXX͍`$$$>۾Ś5kz-BHvv~,WFFFFF&//P__?+++;;[KKKHHÙ={WAA={fϞ___OyZ%B:@o }& H2eŽ{$$$xTWWKJJJHHtV+lB =`ث5z>*N'OXYYQDFFMMMTӧOĨ׷_*3fff\y ^Ulii0aBDD!$""⯿0a³gϢw.""b*++:4gEEEkk;wŋYY͛7_|`HIIXB^^>77Ä[[۵kZXXhkk,^-++?\twzナw#Q[$J-z5D@o!-iH @Z -iH @Z -iH @Z@Z -iH  -iH @Zݩ^<~8))  hk޽ބO6mѢE۷oG|~3ɓ'8΁߿?hР!C 0,$$%%#]]]BA -ޡ{9sBBCCP~ݷ8sLQQѤI6l@9{Q[d=zgpp0*i7.E{V!\tr֭ϟϛ7O^^ԩSvvvS >啒BѨBgg!C] ]bޟ555S-ޱ|r###;;;99{Z u xǖ-[bcc+**vajj:@Z9qD6Mŋ/4-ٳc Z;}tnn*֯gL&}n̘1c֭3gΔ7o/} ;*++QHFEE*++O2eرSOro߾}t:TPPpɒ%?}w'%%IKKSn޼fݺuV-pxQA}з#F,]RPP(--=y򤙙h | BHSS˥SS\ԋ Qc᪪UUU\55H)k׮%ܾ}{ըS;,,,x> Y} ++/X@VVĉS->dOOOqqq)=rÇKKKUTTP[tݝNBV^:@Zc666222 (RFrssޟx& }DDDy>\\\u ;3fKK˲iӦ=x 00u ٧bXenTy;Z `অg"##Ǐﯬ:c=U=dn=_jhhYp_]ZZz̙Z)@0*(EU\ v!dʕQQQO>]x1i񎄄}544\r%88ԩSIIIiEϝ;'%%u%^IXXSqq1Uw^!''_S?䗓ckk+!!abb49N AAA5rHh/:t… Ƽ;vtժUh??DGrvvڵkGKK Zĉ/_b BSSƍz$22IXXx۶m!!!MMM/_^lɔްaŋKJJRSS7o,,,`0կgnܸjccCQTT|OO>MIvv>ZFFFFF&///;;ё*/G|OHH_.\HG}Jkkk ۗ 4-]h"cccyyҜ .?D^^jIII W/BL1 (:99Y HNNVLMMMUUMm۶=xwK,IIIm`` 𢢢&MMMLӧFFF bbbTۿ*3X"z5Yi/`ffYgȑ#>zO0ٳgQQQͻvrww dX3g[ZZΝ;diiVir !'((`433111sΥ/^ ^b|nnÇ !k׮ֶ>*-++?\twz_ǏϞ=G^^d?}eƍjSnݺuhhhƢ>ZZ 6,==իW%%% C[[FN`w)((`111oooT+o3gΤ>%' /_| Tg_'zuu5-M}@Z#99YQQH ;^ڦd֭S;wKCeXwF|fu{\._W^^Q[[P_?ϟ?3:: _ B\nMMbAA}wlݺȨVSSS^^u xǢEL"%%wȐ!垞SE[t:իWl6ܜR]]j@ZmϞ=;v쐐2T+oO/Z|LDToэ3flݺu̙*j@Zm͂~~~oxGee%jKPLS|L] 6US jjI.Y!bmJ-8$*!l֫i%bB  "tCd٦ESSF?|޽ CNN믿fل[[[ TTTl.\ DGG%&&Xt+Iv҂VRҢZFFM˗-[d27lpŒ͛7 1:UUUJJJǏ"dggSgeeeggkii ruu fΜIt:WVO۷߿555Tauu$ G}4z9}rrb_66ʇjbjjjq6^QQabbBp8GTT 33ӧFFF bbbTۿJ{9هb_6jb}#Qιl6{fff,СCsQTT޿KK˹s皚,--_JZ899Yf̘1 #11? ڮ]B[[fńhYY~ҥK;s6֭[nFll,ZK[ - -iH @Z -iH @Z/3}*UvJME * зݣ ʸǢPcWs{>qQ5H @Z)}bԓ[25*oaTB?AU@Z|zj&@Z赖wrSV* з0*!⢚w} O^>hè*зზH e{iH w.7 \xP} AU@2=M H @Z - H~t|qCH e{ -i^"3[@ -iH/П۷yeee PWWנuaoo6ӧOl}vvb ?COGK~ 3|~ [p;Go\͛!`[[|,Xd2͛АjiiIqss۱cΝ;`hhF2eʦMn6lɓn;wuu\QQ驯ohhsNB_xA9H}}=!СCW&޽hʕ̈́SnٲeРA=Y*11R5kl޼[N[[dzyyy_t4j(Mjkk'Mfkk?++kرFJHH۷/""b޼yW\p ###AmAsԴٴiuwvv? xϜ9c``=v|ymmm_|IMϿ...:::ZZZww!Cˆ ,hjOQ_ !DZZz޽ uuuU} 7nyŋ/_TUU}1_7oތ3wǏS}adddJJJFFƅ x3yfΝ?~+n~~~fff;v lٲEVV6+++11ܹsaaa< ō1"11?~ׯ;wѣG_>q!DXX8..ŋ&MرcCBB?_ϟPyٲeW/_KCQ3{yeff?~|֬Y?lڴIWWwwwol6{GyieeeIIP{ꁁ999nݺ~zgSQQtҰ/_޽7??ԔkgM6>zرׯ__pիW !4ELL1s7nB?~,$$4rHWWGtQQQKKKrw7o!dرf[[[###BȜ9s.///++kɒ%sss555_5|E}}ĉ`:I}&$$$++;}&/))毓Xwww!!!iӦ_}=zd2544:EEEYYY1LAAϯ[Kh3f̠hU doG>py<<<>_ "..Nx]uyRRRd2?%%%KKKUTTz\AAwIKKspp7n܊+,##SRRr 3fyCӧOo]I)++۰aógh4Znnʕ+;{ׯ/---*d0 WuԙRWWYryyyޥH߹^ eeeNpO VVVjhhW^^ռMKKKKKy5ɯDVVzMR谮:i9s̙3<==O8aggA^Eee%9HEEڵk_`0ZZZ)[YYd}6<>>}^ &ZJKKŋݻ/_>n8BÇJNN^O:}ԨQW\0aB=$$-ׯ?~O?t!??7okȐ!'O600077wvvuttdٮ+VΦ'88_WWNRRyȑ=v؂ LMM544$$$zVQQpqq:u*Uf͚7nX[[l6ӳT__ƌwfX[nꫯygΜΝ;O>dȐUVy{{WWW7NSSzJ&ӧ>ܷh[|hӧO5ku=9|pFFɓ'Q5RDlhMMM۴]zzo1O}`nnNErPP N|}}=yQ >{n`$ ziH @Z - -iH | BQЅv!~x֗IENDB`fasteval-0.2.4/benches/results/20191225/fasteval-interp.png010066400017500001750000000505061360145762300214110ustar0000000000000000PNG  IHDRO$obKGD pHYs  tIME .# IDATxy\Te}MdE%VQ@AL̼ռS23˼-+JC353åVAAT\@-T\@TAeGuI\_χ\3s>09\:*xb !NJ!$Xf*U 0@H  $` 0@H $` 0XhҥKݺu۶m[{ݻwɑ>UTTTM׿{۷okKKKݻGvv6?<{8p/INNNOOo:q_zIqttc---gg]>ҝ?>!!aĈ֭۷o*^aPPP7mtx >ƶ?~-/3}²B驡)SxV[[uݻt#~SLy\ao߾$`UUձcǖFGG@JUUU^.WDn܇vx=z<00}Ga uuuB333ww?/5-ĉ[n:ujYY d2Y^BBBv-8x  !n߾w…۷okkk=ݻw?9sf…6mvڸq>#sscܹ333[nÇwpp^w%44_e2̙3GU(&&&}}grrrV\)stt:ut.]*++ڵkPP4ΡYhΜ9sU߿̙3EEE]t߿߾}_|ų>۫WluuuQF5-?xE!ĺuӦMd˖-y7o>}EbbbRRRjjjufaa!TUUuk8Vqqq׮]200pqq 200h%633suu[⸸rmmm[[@i/ .Օ:ر#))iРA#FZn߾_la'׿`}}}gggZZZ:wܙRQQabbҿ馇kkk.eSNwڵoݺlٲ@cc㘘L;! |QF@Fyɹx񢓓 oܸZ]]}@BXZZڧO===CC>}888do&''˫k׮ϟ?z"55ƍUUUzzz=s挛۠AlllRRRN8!ѣgϞݻځonffЫWGWSSٳ/Quuʕ+\ү_~YZZ9rĤIHHА%(kמ:ugϞ ޽{VVֱc4445558qℿA͓RSSﯢҨڦuuuyyy ILLիŋ7n066~g---K.eeeijjڞ0vɓAΝ+]fdcczܹz333{---9sd~/_k׮zK!lԩPRZZK'~ᇄp!Znn!C*t޽{~פooo}}#G\xյw***:::vM##8Pjի׊+{4tԩW(O|嗱>>>:::椥EDDHgmllO<2 !App˩?Tׯ'&&fggK)|۶moaƍ mNƳ:::ޭ޽{+**^{5ezꩯj׮]3gtrr۷n;~xuu"##CEE;]߲T\>fggg=**ƍw{UuuiӤ=lٲxoo{MssGJGt\xqԨQq~~~~m\\Ϝ9ѣcrrrIIn۷*{.]TWW7̙3mNNNEEO?r8444^|_BpYBѻw HҥK~6iӦI񷾾^PΝB4 ZZZuuu ޽{?KB2ٖH+󝝝kkk5޽jҔtY^6Kg%R֯044P8)(Lenkvb]2)Mfee)[ͧOo5Jήt RRR...5TTTիNHzkU^+  䓔0gӥ%%%ML&kx-alp0͛7[7jʡCeggWVV*[TUU6JOivJH-8qpRk )5k+}vUUU_`333 e2Sbbt7##[nW\qvv}vAA4d;ѮoY~9{y''';;;iH 655rJqqj+H 7:7pYBt-$$Rco 5P( _u%Whhh.] M]paFFF]vB|w: FO ψ7FMTOOO%VJMϸߍA-upvv>zhffsFF4ʕ+B鬧4꣕;/Ms&~#G9s&>>>>>^OOo hUF# _n!򑣢2iҤf7SUUU|HpI1B.K/.o266n{%$$!&M\.oZZZ Ӥ[--%[H[4KKTUU\ҥK/vvv+c'СCZRRrCܹS[[˫9m9{^2)[PPFV.Y^^pdB]]]III ؤ {Je!DQQ4ի_BP7lj&4^sFu ҲvtW^.geeedd8::JNK|}}KΞ=BFUbjjږCZI:mfeeek~jyP x X:\.WbŊ 6smǎS>NIIQ(ӧ0_-`[t"''Go rʢE>ʕH۵pgz<55UrlGGG񝜜/_ܥKħs 4f'U׿K>UǏWfL iv{Mw^IHHPB?ӦgvD Xɣall"Mjbbt…}ٕ;vΝ;щ' mmm 455[ihh8lذo_SSɎʻoܼysϞ=2l?wyyybbjfffbbKi'N8zhuuSeecJKK_|VN{y"{{=zڵkׯ_?`7n$%%r 5x`SS#Gٳxذa R ݺu#G PYYyɨ(SS{{{wu˖-111Ʈ6I38p 99֓&MR9>멧xbYYq *7|s߾}l l\ 2,--шg%3gƞ8qBGG)((Pkkkmm*]jjjpp=w_կ_?--G>|JOO_KnJFW^۷oߝ;wLMMǎtӟAeee主d^{-!!;vҥ˸qN׬L%22R pGh"--sڃٳgĉ 'ţy攔9s0 YF*-d2Y||>>^^^ov]]bFo<8cƌh!_=`555-5ѣ,99988wРAG%qz뭷9svجY͛G@{陕S__}'6w_|@MM_ׯ"55U3j(!>zh!fffǪ WtfEEE-JOOWQQΞ4iNTT7|駟/]Fٿ 66vƍBBann.>|xll';)(oذV.+U0hRvYU\"E?BܹSMMwޑz5k._|RޕXXX̚5_o#F|~~~zzzyyyNNN&ގ_!č b{jAQQZjj#G***N<9y䪪* =zHgp544ʄCݶm[yy޲e˫ **jȑBRLfcc#׭[P(*++I0ĵ8̴*j78p͚5 .ܴiS~~|||6mڴ`!DHHu 6l>>>у B:thСB={2$ $$d>>>&*SN[:gR6:3Yخh{تq%k֬0,\ 6@s:i8v0 <mʺX6:m̌;yتygFA 2ZWu4 t.7[Xzť $` 0@՗߮HoLOљB:U+bwϝ;'%%5l\vŋ/_މk(H~#}+nS(0ҥK B$''4ѣ ;WUUM>B|gϞ6mz饗V\I@{Q__?mڴ3f:u*00p޼yBsN<رcf͒ZlْiӦC !vYWW'511QSS~}˗WVV.\~!!!BS]Vᅬ=Z/UJJJ>|Appȑ#BQFI=G!J8bƅfuu#FQ$>>FFF>$`_IU*ЁnZtn JuTTTxxƍ8d2\n:BpO?=q۷oݻOaoo駟64 0܏#&z7W49&P+efjYΊ)}PQQYzƍܢ?쳞={2$ $$D:+<Ǐ .B=`С>|Wy.g^z֬'Wܢ5::aK0tuu׭[t%AAAT墆;qߥy}7|3yηihdMظ*z=һļ&L!Qp4Z=$$DNSb,$` !*.ޡD ΥUr{<0@sAKu%gZXf = ͮt$!>0 ]̼gҖ;( Ix"899\W^wYzQ233.] ׯ_XXXVV FIIɅ V\{رW^yӦM1cƩS͛qʈ#TTTttto.ɹ~zHHbԩ'N|Ҋ9`Xu𬬬L&#3PQQ}([222d2Y޽Ƙ'''==7oJK,066>}\.k@n/n߾3ϸ6N8188dcǎ-))?~UgΜ)vڋ/._C`ҥKkkkj__ߡC^p!..fţG _~}fffqq+ƍkf_^ŭ{)CC;cǎqjkk[W_{uVuuu}}͛7 駟f8qIM_^eG.L,oLe`-'5sܹ111W^]hѾ}.^K&edd̙3'??_& ~뭷.ow_ZYY}7VVV$",,QMBBB!C4!"""""➯-Xfi}2 WU;RvNMMΝ;?<}ۏ3&..矟6mڄ Ə㡡{Q&QF{}/s"%y%C Bv^aggw֭ׯgddB.]MII40,h _s4ev IxZ:<  $` 0@>_p 곛4]do`+q=چڃ:݇qWKKK[[[[[k׮RcLL^PP͛7%KXXXO>]. !222d2Y޽[]F3B|+Zzʈf,rpK'rΝ#S%%%Nq2~UVϜ9SyĉsEFF !&N\RRxch<]Qqei!*[X[Lѵ{,N66l X`]mo̙3'**֭[ HLLdWbԸJЁ\t)44_~aaaYYYB`__A=za?駟IBIIoݥKzjBtgggiaVVVFgg紴t{{{uuHέ~ڴi3f8uT``ysΝƲwQcAAXCCCCCCw 6rBq$3g(/TrqqUj[[[/JϟwuuuvvήёgϞ-,--q hS\nE&{o#-_̬XPdeedX''\ooF ǹ:::Ɲ>`pmmСC+rف:… qqq555/=zfxx333WX1n8333K/վ|:**Jq?T&u)Jeg}vo.))=ztFFFOHFFFQQQ󎑑Ѽymfaay3f\vB̚5&MB[.>>>ۺu8JEEe7ntssz9dȐ+1"00g2dȄ ~]52 [lllIOWW-+WT>H SUU? )N[rSNr//n hX<9}l&H-YԩSQQQK.]rرc.\HYM7]$ϼz{ S0ڛ"Xc$=ӣNk׮MIIQ(˖-;zU>}HvnK3 ehw X]]]__]vuttB4[T k׮|ɞ={ƍ'655h4O?2g!ĦMV^MMОݷ~|yf ΙQM]v%[ !v}ҥW^y͛?L%Θ=<<'N<}ܧOYfQV[m͛ʧeeeY6mKJJ:4sLj vu+-tГQG"cc?ݝiWngBCuz˗//_1n˄ ƍ'^z%MMMW_}뭷Z-[޽{˖-?}6naƎ$._ v7٧V~E?c^^bĉ .|dBBBxlܸq{i?!Ĉ#455Ǔ?ScccOOO8^o3f̈ްa{iӦ^zoʕB]vI/ B۷oȐ! xW+&Lf!DYYGjj?}ɓ' !V\٫W;w40+Wlvl^E!ݭ[vqMM̙3}}}f̘QYY9y䂂gy3foOްaõk׊ x|M;;nF55ׯ۷o_|yeeeXX؎;111Fu֛ojժÇ0B,Y$22pcƌݻځ⋘77+V!444JJJ.\|K.UVV5 nBll[?naaqe˖ijj /0ӧO_nc }oTU]v۷ottcǺwnkk[PPpq/////gyF& !ƍ4rHi }}R鱱qw,,,ҥK.]ZvرcQ}||-Zzjww9sTVV6`jjzߣ4ļy"##62 s !,,,~Fݖ/_/nٲe„ ]vBKJJJ `!D}}}w߭[nڴiB555gMMMMM'444447駟^|m:\\\\Gcњ[1?صkȑ#?\"HNN{㏧L2|'NBܹLMMM_uQQk׮.]&~aٲe B]]Ny]ˏ. !#""Ə;͛O8dɒ9s|4Wڡ/74ԄWrV^O* tҥ^x᯾n|QQQy曷~_ަs @D.s$Oy-Vz-JDUʫ7]nZ}Y[O>ٲe_|6䥗^n v'믿njjvڻi x,t\ƽ[̟?_ye_믷ʦM>|x;}܇6UTTz /hjj^@Sm:lbbW_}Bzmmmj cu'={7իFFF׮]_JYI!v n\hfQWW1rxjEROI}EEsGUT j(YfݻHo߾zBvf'DnWQqA%+**O=<ݝ&u?߿̌ЙA.]L0A *e}kKS(xk$???##~{g)kGc(}ko\#HIO ,HJJիԩS'O>}4e%W4DMM2 !WQQAMi͛ h4 b/ԩSMLLlBM`9~b6*QU۹s^z- jUw[qA:YrsrrryjjSLZUWUrU(3&==YUI ʕ+gΜix\PYPtʔZNjT R«\.4_~ߺu%&&IOO/((͛R%K,,,O.}Lֻw襤$v9t>5)xrŊ[ùKJJƏjժbww3g !###O8yܹH!ĉKJJ/^rBBgff~'s߹sG[[[T[[acecCw]A {>ߏsd,R===LVVVvecӵ(is'OtoEъ>b=Um%mb``𻅒=﹙|RXnݺYZZ;;INHHB|Ǐ%66V_mmm]\\.^(5?9;;RGGGj={vӵYZZ2^<==G\nEĠMfuK?/RPWWv,''|C^p!..fţG _~}fffqq+ƍgfft_~ח@Owuu}v```@@ȑ#MLLƧ>p/y3f\vB̚5&MB[.>>>ۺu8<_{#GӧOaaaxx2dHZZZƈ-666P X}U\%(--577 YYw*.ՙhgfL@M~,TU7vbnV5Ѭ;ߧ] @O6lv{]. !222d2Y޽àҼ\J(_C\\\lffV~7!D|||dd'233ϝ;)8qbpppIIŋǎ[[[^qZH6jܶmoaggg``0gΜ[n%''ϛ7OCC#,,"11:+))s͟}ٴ4!Dzz9---==^]]a#{뇆~999~~~cƌBܹsG[[[꠭]^^ްE^xuzf… .]zuLVVV&55lQ66][^^^nn;ͽ:㇪R6200hK);y|ҎRJڮޭ[7KKKr' 򊊊z-P( BKKŋRϻ:;;gggWVVHgn6KKv{\߫g]˭賱}'ݻOGU;I;GZN`o=7SG8w\ppkr'|iff~+V7ؽiIDATҥKuu/{ 8qį*5kC'M$Xn]||g}uVUqx2u8#"""""$$$ $` 0@H  $` Sd\PYPtʔZNjT '٬|BdԯDNfi} KJ)$` tR\ 8q9ljkHO $` {c64xԒ3_nZ.*jy2">>22ĉΝBL8188dcǎe; .\a˶mx ;;;9sDEEݺu+99y޼yaaaupѨ%==Yz윖nooް@$ܹ-=.//oآldpqqqqq?~|YY?0~xn&&&iiiH,Zl6kZ6kǎϯç?!d͚51euvvY|raMT){s]vBZFlʕ+ !ׯgs% /}Ǒ#G^yBHRRRT*ONɓrTTb'x Ec?]]]'OLOOw8?j-,,8nddD<_-[>|8??rƍm۶]|Y" O,}ZӧOle˖T*\.dt:sssZz%//RUUE9sLwwh?oiiioo|ϴ b)[np.nwaRE6`?ljgas'|1|rʕQOJUUU㧟~[]}}{n yVk!_|Ejjzzz3g!gϞ}ř-`oo^-˝;wx߻woff&;::.]*###- !tx<q^ٳgll駟f ,W&.K*J۽o>aSN b{L%%%}Wcǎ<&**jƌG%466]~,b\\!NǏ'޽SV0L<ϟ?^ ͷH|RN5>>gU<ȮLf8 bܹsZVݾ}{Æ >wŊ6m?gDEE; [`` ̌V(o!Dܼysń .J͆Lc6,: 0[n-,,^r_~IHH@&t:!dz? ς۶mۺu4T*mhh_%88h4zބjd4" @t:`t@ 0:`t:`1N1Do(+zIENDB`fasteval-0.2.4/examples/advanced-vars.rs010066400017500001750000000055501361026470300164560ustar0000000000000000// usage: cargo run --release --example advanced-vars fn main() -> Result<(), fasteval::Error> { let mut cb = |name:&str, args:Vec| -> Option { let mydata : [f64; 3] = [11.1, 22.2, 33.3]; match name { // Custom constants/variables: "x" => Some(3.0), "y" => Some(4.0), // Custom function: "sum" => Some(args.into_iter().fold(0.0, |s,f| s+f)), // Custom array-like objects: // The `args.get...` code is the same as: // mydata[args[0] as usize] // ...but it won't panic if either index is out-of-bounds. "data" => args.get(0).and_then(|f| mydata.get(*f as usize).copied()), // A wildcard to handle all undefined names: _ => None, } }; let val = fasteval::ez_eval("sum(x^2, y^2)^0.5 + data[0]", &mut cb)?; // | | | // | | square-brackets act like parenthesis // | variables are like custom functions with zero args // custom function assert_eq!(val, 16.1); // Let's explore some of the hidden complexities of variables: // // * There's really no difference between a variable and a custom function. // Therefore, variables can receive arguments too, // which will probably be ignored. // Therefore, these two expressions evaluate to the same thing: // eval("x + y") == eval("x(1,2,3) + y(x, y, sum(x,y))") // ^^^^^ ^^^^^^^^^^^^^^ // All this stuff is ignored. // // * Built-in functions take precedence WHEN CALLED AS FUNCTIONS. // This design was chosen so that builtin functions do not pollute // the variable namespace, which is important for some applications. // Here are some examples: // pi -- Uses the custom 'pi' variable, NOT the builtin 'pi' function. // pi() -- Uses the builtin 'pi' function even if a custom variable is defined. // pi(1,2,3) -- Uses the builtin 'pi' function, and produces a WrongArgs error // during parse because the builtin does not expect any arguments. // x -- Uses the custom 'x' variable. // x() -- Uses the custom 'x' variable because there is no 'x' builtin. // x(1,2,3) -- Uses the custom 'x' variable. The args are ignored. // sum -- Uses the custom 'sum' function with no arguments. // sum() -- Uses the custom 'sum' function with no arguments. // sum(1,2) -- Uses the custom 'sum' function with two arguments. Ok(()) } fasteval-0.2.4/examples/compile.rs010066400017500001750000000015571361026466100153760ustar0000000000000000// usage: cargo run --release --example compile use std::collections::BTreeMap; use fasteval::Evaler; // use this trait so we can call eval(). use fasteval::Compiler; // use this trait so we can call compile(). fn main() -> Result<(), fasteval::Error> { let parser = fasteval::Parser::new(); let mut slab = fasteval::Slab::new(); let mut map = BTreeMap::new(); let expr_str = "sin(deg/360 * 2*pi())"; let compiled = parser.parse(expr_str, &mut slab.ps)?.from(&slab.ps).compile(&slab.ps, &mut slab.cs); for deg in 0..360 { map.insert("deg".to_string(), deg as f64); // When working with compiled constant expressions, you can use the // eval_compiled*!() macros to save a function call: let val = fasteval::eval_compiled!(compiled, &slab, &mut map); eprintln!("sin({}°) = {}", deg, val); } Ok(()) } fasteval-0.2.4/examples/ez.rs010066400017500001750000000023771361026472300143640ustar0000000000000000// usage: cargo run --release --example ez // In case you didn't know, Rust allows `main()` to return a `Result`. // This lets us use the `?` operator inside of `main()`. Very convenient! fn main() -> Result<(), fasteval::Error> { // This example doesn't use any variables, so just use an EmptyNamespace: let mut ns = fasteval::EmptyNamespace; let val = fasteval::ez_eval( "1+2*3/4^5%6 + log(100K) + log(e(),100) + [3*(3-3)/3] + (2<3) && 1.23", &mut ns)?; // | | | | | | | | // | | | | | | | boolean logic with short-circuit support // | | | | | | comparisons // | | | | | square-brackets act like parenthesis // | | | | built-in constants: e(), pi() // | | | 'log' can take an optional first 'base' argument, defaults to 10 // | | numeric literal with suffix: n, µ, m, K, M, G, T // | many built-in functions: print, int, ceil, floor, abs, sign, log, round, min, max, sin, asin, ... // standard binary operators assert_eq!(val, 1.23); Ok(()) } fasteval-0.2.4/examples/internals.rs010066400017500001750000000046161361026504300157370ustar0000000000000000// usage: cargo run --release --example internals use fasteval::Compiler; // use this trait so we can call compile(). fn main() -> Result<(), fasteval::Error> { let parser = fasteval::Parser::new(); let mut slab = fasteval::Slab::new(); let expr_str = "sin(deg/360 * 2*pi())"; let expr_ref = parser.parse(expr_str, &mut slab.ps)?.from(&slab.ps); // Let's take a look at the parsed AST inside the Slab: // If you find this structure confusing, take a look at the compilation // AST below because it is simpler. assert_eq!(format!("{:?}", slab.ps), r#"ParseSlab{ exprs:{ 0:Expression { first: EStdFunc(EVar("deg")), pairs: [ExprPair(EDiv, EConstant(360.0)), ExprPair(EMul, EConstant(2.0)), ExprPair(EMul, EStdFunc(EFuncPi))] }, 1:Expression { first: EStdFunc(EFuncSin(ExpressionI(0))), pairs: [] } }, vals:{} }"#); // Pretty-Print: // ParseSlab{ // exprs:{ // 0:Expression { first: EStdFunc(EVar("deg")), // pairs: [ExprPair(EDiv, EConstant(360.0)), // ExprPair(EMul, EConstant(2.0)), // ExprPair(EMul, EStdFunc(EFuncPi))] // }, // 1:Expression { first: EStdFunc(EFuncSin(ExpressionI(0))), // pairs: [] } // }, // vals:{} // } let compiled = expr_ref.compile(&slab.ps, &mut slab.cs); // Let's take a look at the compilation results and the AST inside the Slab: // Notice that compilation has performed constant-folding: 1/360 * 2*pi = 0.017453292519943295 // In the results below: IFuncSin(...) represents the sin function. // InstructionI(1) represents the Instruction stored at index 1. // IMul(...) represents the multiplication operator. // 'C(0.017...)' represents a constant value of 0.017... . // IVar("deg") represents a variable named "deg". assert_eq!(format!("{:?}", compiled), "IFuncSin(InstructionI(1))"); assert_eq!(format!("{:?}", slab.cs), r#"CompileSlab{ instrs:{ 0:IVar("deg"), 1:IMul(InstructionI(0), C(0.017453292519943295)) } }"#); Ok(()) } fasteval-0.2.4/examples/ns_btreemap_str.rs010066400017500001750000000005031361026541200171160ustar0000000000000000// usage: cargo run --release --example ns_btreemap_str use std::collections::BTreeMap; fn main() -> Result<(), fasteval::Error> { let mut map : BTreeMap<&'static str,f64> = BTreeMap::new(); map.insert("x", 2.0); let val = fasteval::ez_eval("x * (x + 1)", &mut map)?; assert_eq!(val, 6.0); Ok(()) } fasteval-0.2.4/examples/ns_btreemap_string.rs010066400017500001750000000005141361026534100176170ustar0000000000000000// usage: cargo run --release --example ns_btreemap_string use std::collections::BTreeMap; fn main() -> Result<(), fasteval::Error> { let mut map : BTreeMap = BTreeMap::new(); map.insert("x".to_string(), 2.0); let val = fasteval::ez_eval("x * (x + 1)", &mut map)?; assert_eq!(val, 6.0); Ok(()) } fasteval-0.2.4/examples/ns_cachedcallbacknamespace.rs010066400017500001750000000016761361027011100211740ustar0000000000000000// usage: cargo run --release --example ns_cachedcallbacknamespace fn main() -> Result<(), fasteval::Error> { let mut num_lookups = 0; let val = { let cb = |name:&str, _args:Vec| -> Option { num_lookups += 1; match name { "x" => { // Pretend that it is very expensive to calculate this, // and that's why we want to use the CachedCallbackNamespace cache. for _ in 0..1000000 { /* do work */ } // Fake Work for this example. Some(2.0) } _ => None, } }; let mut ns = fasteval::CachedCallbackNamespace::new(cb); fasteval::ez_eval("x * (x + 1)", &mut ns)? }; assert_eq!(val, 6.0); assert_eq!(num_lookups, 1); // Notice that only 1 lookup occurred. // The second 'x' value was cached. Ok(()) } fasteval-0.2.4/examples/ns_callback.rs010066400017500001750000000007351361034103200161630ustar0000000000000000// usage: cargo run --release --example ns_callback fn main() -> Result<(), fasteval::Error> { let mut num_lookups = 0; let mut cb = |name:&str, _args:Vec| -> Option { num_lookups += 1; match name { "x" => Some(2.0), _ => None, } }; let val = fasteval::ez_eval("x * (x + 1)", &mut cb)?; assert_eq!(val, 6.0); assert_eq!(num_lookups, 2); // Notice that 'x' was looked-up twice. Ok(()) } fasteval-0.2.4/examples/ns_emptynamespace.rs010066400017500001750000000003651361026523700174550ustar0000000000000000// usage: cargo run --release --example ns_emptynamespace fn main() -> Result<(), fasteval::Error> { let mut ns = fasteval::EmptyNamespace; let val = fasteval::ez_eval("sin(pi()/2)", &mut ns)?; assert_eq!(val, 1.0); Ok(()) } fasteval-0.2.4/examples/ns_vec_btreemap_string.rs010066400017500001750000000015131361026576100204620ustar0000000000000000// usage: cargo run --release --example ns_vec_btreemap_string use std::collections::BTreeMap; fn main() -> Result<(), fasteval::Error> { let mut layer1 = BTreeMap::new(); layer1.insert("x".to_string(), 2.0); layer1.insert("y".to_string(), 3.0); let mut layers : Vec> = vec![layer1]; let val = fasteval::ez_eval("x * y", &mut layers)?; assert_eq!(val, 6.0); // Let's add another layer which shadows the previous one: let mut layer2 = BTreeMap::new(); layer2.insert("x".to_string(), 3.0); layers.push(layer2); let val = fasteval::ez_eval("x * y", &mut layers)?; assert_eq!(val, 9.0); // Remove the top layer and we'll be back to what we had before: layers.pop(); let val = fasteval::ez_eval("x * y", &mut layers)?; assert_eq!(val, 6.0); Ok(()) } fasteval-0.2.4/examples/repl.rs010066400017500001750000000064351361005340300146760ustar0000000000000000//! usage: rlwrap cargo run --release --example repl //! //! Example Session: //! //! github.com/fasteval$ rlwrap cargo run --release --example repl //! Finished release [optimized] target(s) in 0.01s //! Running `target/release/examples/repl` //! >>> print("Hello fasteval", 1, 2, 3) //! Hello fasteval 1 2 3 //! 3 //! >>> _ + 1 //! 4 //! >>> _ + 1 //! 5 //! >>> _ * 2 //! 10 //! >>> _ ^ 0.5 //! 3.1622776601683795 //! >>> let a = 1 //! 1 //! >>> let b = a + 1 //! 2 //! >>> let c = a + b * 3 //! 7 //! >>> a + b + c //! 10 //! >>> push //! Entered scope[1] //! >>> let b = b + 10 //! 12 //! >>> a + b + c //! 20 //! >>> pop //! Exited scope[1] //! >>> a + b + c //! 10 //! >>> 1+2*3/4^5%6 + log(100K) + log(e(),100) + [3*(3-3)/3] + (2<3) && 1.23 //! 1.23 //! >>> 1+2*3/4^5%6 + print("log(100K) =",log(100K)) + log(e(),100) + [3*(3-3)/3] + (2<3) && 1.23 //! log(100K) = 5 //! 1.23 use fasteval::Evaler; // Import this trait for '.eval()' functionality. use fasteval::{Parser, Slab}; use std::collections::BTreeMap; use std::io::{self, BufRead, Write}; fn main() { repl(); } fn repl() { let parser = Parser::new(); let mut slab = Slab::new(); let mut ns_stack = vec![BTreeMap::new()]; let stdin = io::stdin(); let mut lines = stdin.lock().lines(); loop { eprint!(">>> "); io::stderr().flush().unwrap(); let mut ans_key = "_".to_string(); let line = match lines.next() { Some(res) => res.unwrap(), None => break, }; let mut line = line.trim().to_string(); if line == "" { continue; } let pieces : Vec<&str> = line.split_whitespace().collect(); if pieces[0] == "let" { if pieces.len()<4 || pieces[2]!="=" { eprintln!("incorrect 'let' syntax. Should be: let x = ..."); continue; } ans_key = pieces[1].to_string(); line = pieces[3..].join(" "); } else if pieces[0] == "push" { ns_stack.push(BTreeMap::new()); eprintln!("Entered scope[{}]", ns_stack.len()-1); continue; } else if pieces[0] == "pop" { let mut return_value = std::f64::NAN; let mut has_return_value = false; if let Some(v) = ns_stack.last().unwrap().get(&ans_key) { return_value = *v; has_return_value = true; } ns_stack.pop(); eprintln!("Exited scope[{}]", ns_stack.len()); if ns_stack.is_empty() { ns_stack.push(BTreeMap::new()); } // All scopes have been removed. Add a new one. if has_return_value { ns_stack.last_mut().unwrap().insert(ans_key, return_value); } continue; } let expr_ref = match parser.parse(&line, &mut slab.ps) { Ok(expr_i) => slab.ps.get_expr(expr_i), Err(err) => { eprintln!("parse error: {}", err); continue; } }; let ans = match expr_ref.eval(&slab, &mut ns_stack) { Ok(val) => val, Err(err) => { eprintln!("eval error: {}", err); continue; } }; println!("{}", ans); ns_stack.last_mut().unwrap().insert(ans_key, ans); } println!(); } fasteval-0.2.4/examples/scientific-microwave-ti-mw-83-plus.jpg010066400017500001750000003766241361033632600224700ustar0000000000000000JFIFHHCreated with GIMPC     C   a  ~4"ҒK%Z*C`X0$0CFFX`V2Z#$R R)Xj,@JBYP`$eKr&.j%T*MdSbPiTU*[TXR TD60heV 5 @$#HhB i!F@PY V,Ra R XJ JU !<5Re(ʪ,E(*5NhĨ` A`@ģ!jF"#! TѐRD*PH )d   bEDUFPaeEZ QDTBH*IE@J$5 S$5 Z$!4! 50Q$B"! H@H BH TTEQA!hB$ \-HTXYQ%Q)`( /WI H0BA0h#$R#]$FR "b%H$"ĄX$P("hEZ$ZZ&RA!XQDŕNx6A4d4e# iFV$F%0֔0fB5 H@X@! B @(@QDiDDB*2,TdUU*e@VV*V(bJ,:6KZR#J0H4B0H2F aiI+5 B `HB!VEA@iYj++TXQDŗc) LCb`MjPB!X`іR$4C!B)! B  AP(*QJ ebUI+ZDʳRAE -$EX:a4# CEMDe(PY#ke#T!6h*0BB$ P`H@ PX("X rXEaUjJTk!%AUQDI/[Ҳ!5HB5bQR IHF !0H4) m`QBT(@@,ؔ$P%Yl@*)UdJn{IRA )Xu# Vj0, XBdVХқqj$PYaǥeVG XrȏlC b!!sֲX,CzcɆJc)QUAD- M˿]N5&XADJHX:e@}1W~O`X0ťBbXOd% ZBdWR$P"QAJ$"KTPXUAQZ[7e!RM1+Jd@hz*%Z%w:u.7+:AQJ|VR5[ ` o:.oe5s2< R&hdTl r]9xfRЕSZb,WReˑ-SU%o3HILA萀 k)kysxYeÙ)^pYLWUێy?mmK/M`f21jsQiL|P Nxldt&S6]n.͜NNl2B^.gUYtM}Z]嫪ClO:{[iO:,ksr1c|ng[脔FŲVb[aqeYnY=ǹpa}IU)ƇzȓB_z2L+Ptׅzw}X9he,xZns負_m|z8i7_˦Y=7/CۗQzqqs:ߗ[˦Y^}W>Lv?zy>hrZ%JEtXS2*xZΫ=X &UTR(Rd"P i,`EZh%DQb"!]#bY罎uy-n/.9-?/,V`Ԉr]L\UEBYχϾuϦ4·:)(ΗD: aQyYZbo%@ H@ BB! h$eVЁ!+3zo/jMs䷟|={z 5T]L!@CIu $[v2Z11χ^c,Άj)Qz~]+*:s2,kWYBB$B@$!B HB HV(Z4eVB1 AM^Ʈ/)Yu)s7nȊ+\qLk&ޢf$E3x7}={k7ƱcE-DJ?.$UU!is&.Θ!!B$!HB`$HE4RP[ \  1#K^^e[]6t{3s?=׿|r!$Z 3nv0UgJlp׹kƱ mLQ-JW."-b_NfVVNKa `$ !B @%@Q B-ͺmXS! W)1GX2r+(kzs ,#ǚ߹8-cLEz&JazoӌM.;zNJbmNZʡ*N2\5qT}A׎>I-$!@B @@RaQ! ey H(LEe4_E.CY'MLyf?Dyyr{sYY4+ukצSm:\@!&]{k~ÅL81DQ)rZT$WUOLq~MWn8:ac!1  %B>nNwVy R#ՁFi=s.k!rϓ<\>q\j3*%_g$/.9zib*&uzs#ښ}gΎ}?9A̒$BB(HB UEQglDu\o i^TQl\(c,_Xz22Θ77ϟOܸ޽5PdiQY ɧWˇ>o1|)&v|ԕ-eee'Uyr5W?J嫳1K"T$ DЦɅ$J`#QYD+ -4c+2#!}_鼽r>7}>qw+MX Qƶ4BA'c󇧷a_jb i)bڴk+*:~h`^}\uTeLS-- ;}i)*J#aFkeq]ケhlzqAez`Q  =eHR$ 1*F11 ν/m2ϊ?&4v`E0BILE5YFR}cS/Cn<~}ffNąЄre 6 F #  pȴ`IYag%rӦϊ<\i=Lbr~.zcwGLtG;wyLy:j5ˍv65gZuSCMPPVRTLfozM;qa.fjJ,-#Q FF #R P2)L8×,2:{.޲W!rw#|\[==sxfuɢp7=UwγjY^N/\LS|i߱9c:☤jmqֈ(+M\2qykerǗґ0F3%8SC 1B"a@`5EX]Lk/n+md$W,,!("Ae*$=lq.n QTVnF 6_/M>׏1_`fJJ 0BBJaA2AAt@"I-2kJ2:{j2z:}siM$P"!pm\:9oE^jOqkeE2-LF 0MX218=8B076jA`!A QP:0HB JZPALǓ k_e>.e}Fok *Ifǻ3uk6-Ӷ S#q[NwvzII]c\7 )f"=?JLs6y]Z|:1KF6(!j%!2:e$"UX  *A@eLtΰby8r/ۡ8]a,O/>3ϝ=!9I+41GfkqNn㞹GSLQ.LIAPTm:s#[3 ڻqk!cs.}cRQdÐm,!HCRE]h ưʊK ,܍qzY|W~'j*k,uKjsZ3xp]tΜOkYbE2ӛIJǩEQPRls*koF}|.5Z@sz B :[ IMBQQlPj @`VZ%0˟NˏkVf^S>R̮;]29F^ظ]:(N7zsu*ZW2=_*2s*Vff^};Sqh*k6ZA@%4XM !!P$ B09ubQVvcEMSE&}k9ek{7t=y{gN44nIefZ#}}Rjedܙ{c\c3UIJ\ֲ^2XxcۺMρ%q:tճLBBŁ! !" ( @ X<6u,&Ԧ)0!k~ܚq8N~gyCiXj}ox}X=v_g+[9N7j[e4̼)=>.5-2PTl\ҵ)*̄fw p(,2Hi&c@уFY``EZ% %!)i,!Vf 5FƉX]eKIu)RR ՒZ^zBsޓ1jǮiYYIQLcZ(˱3YܺOϕ`aw!#hăB-,9*Wm)UH%zT!bj(صcVcFVf&z^];ʖ,uϢܩ1͝`ut4奤ǎͥskWdi c}4x?-iJmcҲ)^U#5Y?uZ i. U68%VXX(ÒB(K d/ Mg\"5C˧{˘~4&o ]c]wՅϼu y-/i1j%6kѣtgZΫJM%%FL sNuݽ>ny?=J iLttX1SPi0CQ *( 1XcLeTLh…!YytwI*7UwiLrɣVsi3ƩenS#S:?'e*_}7j8YPQQRkdReW+i3Lǧ?9fJ=ud[1H0bSqd+$[`dE(@AHdYA sr1kS7RsoI32sۖjJL Ki2M͚ŬŌwfi:;[~oz{E3UEeFObZZʒ5}0 7{4:笸Ä4#o]aeHiiinK %S$1`@+@q4 Bʋ1c%ާP{_^c-qVbNUJ3{ǭ?}N{DqQQAoYg'gm^muwLks}NsLEe%-&=nPPo˵^}k}qˌyr (CV$ \Hdbj$d"  d4@ TV^yGmK*̛NlQgk=O=5VxuF*}̯o\e˭wfv5|sxf>*JYYYR`(ZOl}4,q̜ڬ!,P@0bՃ]!@RDQDrR4- A-)J)UR(fox=’Fϐr:VLf/.}Vc'=y{X[dMqUk޹fTj]>ī*B-eEk0HcsrWwNbxRЀ6 R@M!(A!$R(Ia"C %P1B77<%Ws+3Yt鄳'A\̨{6obrU^BhkS׳z4o{aJyo79@UG@(U/;#׏޾-N?:b` @B"!!  $$"RHG1\_/ϣZS] 2?^K (4xTgf ]zf/k>Js~vv_Gyr_7ǧ5|ӒKQBU] >b 4!Yo0fZkn?aw*QRXJKAE+D*P$!!B!EBPac"1(~ܦi̾:,Fm6Wˏm1u?MVJ`k3c9Wfcs&+OsӚG˭|/>7n] ϯ\JKo6n]]H{ry2Z6HPe!Fқ! b eKZTWIbEjRQX%Ee1M$TRQeeepfISgΏNmck^mʬ`Ki/zVop)xW<_S'2z:a10oo<>ϡ1ӟwϮ_J7kxtKf$O(@ haCdVY"Q Y1Q AH KTH"Z$j_ӱ|g^@Mhvw Igѕg5|[(MZCy@Q)di.jزŲdžCQF "1(D`@ JBÒq,pooA@2+4VZ돳\Jyce+<z֮MW<:|<ݪ흖~rٶ}]O RJ ض bsr1 7:){jRش%[  .ί٣`jrׇil[yl$5 dRHB 0D/|ze0~|k9Og:sbSAH5@ E(BU=4q}y鳽Gz5%͸˲˛˷ Y\JP(Jk[N4okڕ:O=7w(WIeW5Ub4Kyӝ:" ^+QH*E$I, 4+K`e2+fc{??{uxƹe0IʭDJV R^[%[e[oa˦˗M>;so|Y)3,msՄ$Os +MyK" YYA8zDUe;r,YKX["_;2bX, * %P].p]INQmLSO ,\[w:ebeT6VΘ#6"i.e̖K_+*XJs 54P怃4زsF ܯ[Y RD 3d5De\ITՈYE jXLVUM@5RfݦŃnJXYfKB+Ʀi]ͫrJl)VQԐuUl1سs"ܨFcZڔ>ə ӝN= dsllS[ ji-T4#Mw^-_cWٯr V%afu}L+G-"e7lfF{zSAͫ [=ozmv>'ɿ‚bTQ$D5:w_gn}Z~"UeX ylU%FE˵jk~q6GZ+X.`0`0cr7Gڂ!&&o;A}-ODzVg:{Glh9ŁG3jgW]*SfzΊ*ݟ55)? CXR 9()&-F 0TS()uu4[l`|v{Johzqk>Z䂰AS7iX=ŷs`}[zV[X[+bQS޽ Yt.bBoLPBZ~N}[]efMF#Ũi):S]oNLk]Aee}]41FS---=Kdt:I5#UomZhQ6I+"V5,0RZw Ƈ77ᩡM4)M hSBХlNqNAj+/6K\n qv^dv>lUNSUG`j`^>8)>?ǧcTW,r/_J]~AŠ;w_B옴*܎\g/EAM #8 8g30000CM 4! iCJPi0`II 0` 0` 0K&7\džkjS7#C&L2gudɒ[6Km> ղx]k}]ػ"ҮFvvvvvvvvvvvvvvvvUں'k(NН;[Fvu)ckS'l";c4;dvN*Srlf;c1 T;aTv¨YՇkkNW;U^vEyktM ~JA`I靯j/)Tllr"Փ&|rdɝJk;dsTvk)tjY_EپRټp_-VROA%:3^;nwOPwץ%bkfmyׅcG]d{aF5o1]awMTmKVWHК叇p1OS<96l>ճwyu (nQOs^F{2j2dɓ&wdɓ&L2gv{_&D^gyb 5ZXUYdU LUkWTPP:3C>Lɓ&L2dEK-ofu(rK&^\}xIV710`csD{L%_%"n)J #<ʘnܽWhKJ\<)|&L2jRz\/QzypܽQFw(u??)۪x[fOO{YX8bG"ɠph4̈̚|4 Æp3&@<1"8y8~XV3pf "OO >34xhS+Zt5O7cK_gWrT1,u\NI-jeh\Zj7SHɓ&L52dȋs_E٧rܢ3QP](Oy_ {sp.~|+)rߍ`{ވ`yLAL/-zOn&Fkhcү^‡g _r.}8T覥-D2dɓ&L2gr/]jGٷո{nQE (^|w nw&7cWdž ܈*݃AE%O>JHdAm ٳڻ^gY-2Lv:ᢉ Qt  3p+%Ʈj#(TQE@xK7ܼZ?&~71ݝș1}cu??A _6cگR{Kvrvv #gg4A:)iHt CgA: *НvM 0 w0cro^D7cv;ޢ8xl߻EL 4s=SKmbQ̗TkZHxmdLkTn族5 [i^ϏkXVm+n,5-#'dIOU_WUwm(&=3CtmeUR~uSrܪ 9Q{U5߹<}oNI_5޽ޣxO4AFMgO'R5W9#fd883ԇs 98}:1MA;^ No^ T'7"l{ ӂӀӂӂ]$h+tv!Zt*1Z>BQJ_v(OTo6D1?MP_N'y&3vܪ*?GsL4o;UCMajNVb=涠EH=꥕y|ФzQE)=(*n^O)4mnAFw)[OOx*ނn^sݞTި;z?l?(4EE=oZ]:e -/..~_8$1ƊdWvAcAԔTC]+MJ2n\'UMnthv__Š(QEAEڽ)FܛR'TA<&1ؽwS7?{ަ(TE/^FC*F;I_~J.ȟ%rEYH߹WGY6>vbZ:k'TtKGMCCg}w6[,2-H5*MURG3#(t[u(csu -\tClc\[[3^f]}Oz7 (QEQE!ksl͛^ٷO.4|yLJOܾ_֝g ~7P{}8"g n_d)'^*rv*,ҹ2"4WY5ҙc^r_)瞔x#di[$RH 4jHxq+59(iifFԱ$5pR%4D67ޝ'QE]֟r(o L]<)xR=3tY?="wS&7wU,^H&IG5?{I;$,2t>ۅK~dVڪSLG W{IQERj.]麃\=fw7ۗ4fZʹs|!'qE]* ;r.17yw "EQPpJ/zX{ ܟIUǣ7&kM)J> F'`$4pMKǖX2IL)++ N8q IGD\lR+v)=(|QExV6qepy~rm3[1Qp~p's7nQW"/q ; "S>*8xRj"ψ~) Ț1G;SG65ҹ懑)$!qRUҔJAG58VgW(./ʋE^'+o W'7ۼhr"?B#7&o_dQwx M߅L{rpEܤ~Uv?~֊ԧMUsNlg܉*V!Ĺ:SLnBJ!cd}|1p()dQw.M)qpo,MSN6^_+ήqU88w!8eJ+" 4|*QxMqUN"v5hwl"i{Ƥ5&5HgrָA7i#/.-'tdb #TF,JǥL8I LqyIO8V. OPTl%,ܓʛ?m'QEQK'QE\M[ MC;r_޽D?(LQDw 窊UD\EU2+WnO Ȼdɓ>;w8~vLK|ת;:\Dߩԛ7d).掦Ե?a+qy&S+gXl]Y؊qv"MJErr/9 ʓJlRܢ)cUޢ/v/Z "X2nO/w}1.⻚g<չ7ޛ9DV~LIcS#O;_Q' %ƪz˦,20ZoRVU' wM,HYmu mzpluʗݨQKnQEܽzCǮWTFYh4a'0aL)iSIFzw)ɓ'Eߓ'dɝ5UsBڸdoMl>%:BYZvԨT/nmMiuFQDs>f@ʚjة-PIDUM CUIng[O ו7r)amE{_Er.NSzy3rɝ5ԦqN!5ĐώYj2jCRԆPG"Lx e He0.7~|c~ "qEcO?OENEC!EY+lv2l&95q-ꛣb;RKlE:J+ܲ:G [JN]&"+E7r)`}˹E%)]wEͪ]8#DdnUζjaeX}}&aDM0iSBTҦ0aLS x'vE]>W*o2m,|C>LZ啰k }iTs\-ը˖ƑY*M U.q6 pVZ'|"Qwl^鷱eYhgXϿt* TT2# gL*j).ԴlٛIme$6~fe1KASjVw=6Tr-4,1<ܺ{-Wr.vW ?vힹ7^42eWS; 'dji:uene"]l:xz:]b oCT8o4.5@߹W+w@A#P19:),tKvNg+c^4ѺVZn)zGSF|iYvx9,&K+׍*,y)Z{/w8S:VdX6~5mS"C(+~L/G7T={ݓ&L2dɓ&wdkSYL)NZ9:cR(oS[Ej vJvBڧcmbbh؊!v,'ab;.845@Vv&]y 'qAvZvn^D:Er*jšiAE+\CE>/E&ekʋmU"6QSk7H+BmST$R]V [Wf(Mlt8kI኶)MIHu&+3f~NiY6MNdYMṙafT6>53տwLٿ" !fj52YxC <\gvL2ds?Ԧ5)MjjSZ)MjkSZfQj5HU@$t>:KlZ|q6VnGrkÛ%<>=;j+ԪUVc] OS kO'FDžtFJR*+{tbx"i12+*eMN2[Ŭy̼$8iJm3&{ɞLtĽ'?*el5'[HiJkUoխ.P6e㊃Q':yvhGZ-*jWDyu-K1>EWܼXXbJu˨4L!4)8G[&L2dɓ;H:?]7cr}o:~^HBNP* ͞R#,\]пΒu'HRi*VR-fS0hl SMt0$2Ӛ`-Q&A$A]4FFFF + PQLG/Vp+PVC5UPX:KD4éu(Ρsg3vfFFԆ5!&L~}ԬJGYS7G+|;K]ʼT*Q*-kAv1UU R״YWWLs nRԱT̖t]S E&`Y2W x܏T5"E9(N3"9XV#"9HNR8fQ rFY̤+,489N:Vt:BvBigiii.cNFv!6;WLvE OH&ҨEJuq/pb%:ju&APiϴs9q8Û9;3+#Mm$TV=)6"WRGFe8Cq"D8qֆ5 he ̑ќ38"2G-"3517OVvvf(XMF$FdSP`i4EAZK[d,TªdZyriSQjTSvLjCRjCQ )j5ba{&Gs2s?RuRGX:I wu u'MDxY,S(C+\ѸQDɕ5)MFRԆPOD``HX61k"CX7%LjgBe"Aj  i sPG4G0EߤHN TbO^\ܤH**3AV$ZTKpxx0K*iSAi4!C9spN8*pT8G N58fI` 0`O[Ru8889ӎm8q)))CH=ka؇ji@AA3*@dX #J3o@6JB59AQ4rM9MZSXѣ:Ikxœ-)ɪB"9N ųԝEcLOu[}(3ѝ2[U(zal٩ΏL:L>E(g gy-Am k[dNr.97Lp 8B1M*iSBnW ih4M AIH4DMxPt!UD" s(S58U59sSy4c)Ő>>(e 2. BqP!C"C^"S)"Ui4STG!_G} ~KN]N\!5Hp2rp NN 3B֚Zxqs1Qa5e4sjs9vS&5 fY8T8qSqN"GGqqqkqƧQyqq #r9gQ$rn9GINI&Q)r9w 3.Õiʠ$m8 SrG% rQ)Fr1FrÁp Tu(DC蚫 dGR9Q)p8hB h!Zsp9VhrA)آ@9VD~1WDHh ~s ?D#5?@U ĄBOh, !1@Q0APa"2`Bp?GgxB?rvO_I$I$I$I'lI$I$II$I&DdI$I=$d2DQ(VɿҎ DIFHJ2FH#"L22222$ȒI$I$I2$I$ȔJ$I$I$$I$I;tMI$I$I.ܜۓ AAh   "A`7n{ AQO[2FB+m|3o$$MI$6dd̙,hwt'f*r/'@ԏIr9D!>eED'Љ1?URU}e=GmGA>׸v}vG$cO^GБRGAЗͩ'C]uB%f\[Bͩ*>\ ä:˓]Bσ,IQ Řbzl^SzFS:))*G-)Qv9#M.dtȓ1F/ic xRL`C00  C!bLLY1f j7>m Mr4Ѧ$iRiIhF&&4Ѧ:M:M:L&#bQ1F( "h  -AA-_]ϯ𻮽]MJsIi=>qO/^rx=?q=p={ѯ=kk]f55j55555j5QW6lv}~OAy1AApq} {:z^, v}vA>uګ~^WV-cx.V1}]>[}vN]t wcc+/lV.Q_[iUi?}ЮGw[Qݣ&d̙2fLə3&d̙2fLə3&d̙2fLɉ]vvR1J:c]fl͊;/Tu% bCQQ8WhQc \Rz*_N-ESti* GzQdَ>qGD!u%~)=ZjD_Q)[vE_( wWCZZՍ; e]F WUtUԧrzQ{{P_Q^jQ$vT͙VTQOhjw:(kze~*WK%#-GB1Zu_E ڿ'%O[SOF :m][Zu0dM/#,MTjd7%K%⠒F]V*HvVR ű(e$VQ(jhiȠp1/(84j.MO"SJ(Թ|F" 9HGĻF+Uhgl즬WV J^Jv7EJiF+UOQ(W6UbPe6cGztc$ޛT1q?gx8" *|TTTHF4Q4MGh,f4QQQ^ :6aQF,ŐC /b]TgdAM0%B?ŢF bQ0FSӤӧOJIHF4QD4Y'f4*:x[jgdNDjLi!$&K" Ԗ! AĂ mAAEEtWlPmUXRdP>+RNH22$I$m$UKȎ-h  A>XWmAYu#"H`Ԙ`Y1f,@M&Hq H  #tEx Ao vςI&mB@II$snm\5$HlRK2flΣ:FjkTkTkkߣTפ4zgs{Oɯ5?&MJ eA$J8A H8'c)_ɕR4AbbbY1f,C1d2 dT~*G5 JԢD C99 :Q111e^3PK#鴒I$P5 %̟*կɫ_Z&zOs)/Y3332222$C&&&$AAbzHF#(KEWddԕ$311$biH43Lf 3`Y1d2 Vj3Pf5 ST5MTf7ppqiDQ;Vi2"ĽS2fLȂ"B 43`iMTaQQQQQQQY   "AG*҉Db4?MR`D|hBUY]+"d̙2dK99999!bF4h(F4hݚ ШѨѨҨf4٦3Lċ ċAhĂ/$zQ_V'.Q(eI#hd3&JI'Q24j#Q5CPfu2Y3f2fLə3*2fuTeQD*%g$bba0ff4#MhF4ih4Ѧ 3L~ifhOSRS5(NHf&$1``oɥ43L00A&&&&&`1HD# EI046iMlf43Ml4 L LQB!QH4h,4٦` LY1111111111&&$EnFH#*L3ΝDQ$IFH#$d%J2FH#$ffjիWV^ W5V*ٙ&FDہ&$bc|̒IG$I"rK2fLȒm$i$mfLuEΟ> Ex  "AA> 6;#g=g$2*!NIdL$I$nHf,£ j0f F&AZ Ah#gdI$I$L22FffFFf3FHd8888 dK99&&MFUVgYFuTeQIdߓKdI$d='#l̖dc͙*3c+%fLK%NNNId"Y,K9g$3A,Řbb`i3L^biLLL &&~Q(b|x"I6b:M:L)1O[ZIbu},lb+;]l_K-2!1 0Q@A"2PaB`#Rqp?FpAPAAAAZZZZZZZZZZZZZZ4GZYc-e Yk-eZYk-eTZYc,el2ZZZZZZZZZZZZZZZZZZAAiiiA4ZAAAAgAgMGT%N{e9$\I$"\$I$I$I..$I.$I$I$I' $DI%cIG%\I7eG$)>:K8C-e$#ۡeUP^^˙sfdd4ZJKZ#irI$$4T+J'p7JYRRn JYN칧1%.KS'YO"tRU&.OY//{t,禥v_3qn_1?ī8ULtO'TISUS<"b%۔cQ 8d|;cPSKl)\*\ϓsK˃Rs'Y>qLXYc-e?yK+Ο%[INEE. 9yc_kM"ERF U@'+N $Kw" /%I(J%"\DQr.EȔJ*YUvI$I$I$xQlf5Fj3Q^{.e칗2I$I$}=HYUϵyo@kf~ *u4iWhѯ_F~ 54+hWЬЬЬЬ4+4+4+==gzzOQzw=;zwOO4?ChM4?^M^MF4WITHF444444+{PAϻS~ʾzWE\elB6ۢ}Jo #/< s^҅؂;T ϩ{Uٮ}~/j\~z[vi>KJo \}O{ᄃRo:}sBNRI9rs>:}'Or;O!OAf3®:o/nD\KkW+%HY$I$dK%I,K$Kʕ>!}ߏJv#(  `y!v_dp,JQCu=WSآWA^#]WU=/,н5}kvK/3K^-t S1xT$a: qVU:ġֿ,t>,лQ'Wk?j}E[N>5[gc|9}.rÍN-T)f%XabIlcaܳy>Y.'5ҲYON.OUU]ԟpw(UM&%k[N-vDIKC0TRWUJ^o't:xogK=Q%(ף֔a9jf#JC0/'4#qgH_Yثѡzz<T3:-+]" x4 v^XqgNK :te|%1~WS:J'iDZQ2$JW\N;\T佢Oz:xJ J 观st:D.;ֳuM,XuQrlb(XT(y2K?"reo)otSt.FbՈ.7t?hk{߁qgOn?[=N:VK޾}rc>ʜS{Z[ɏ*r]k9'sw=دY׉hn3hXnhes-Z&'65- /&>vOav|҅}|9:iIMxUKFFNwcɏڼ#uԅ]+pa#mwWYT?kVTII9I$g)%\u!f릗 ku,-pQ n%Ju$7.rb*J}AFPA{IRtO )|I$JwJ>skVTA;o$I$Rkg# BͥW#P'W*JOAG|uR!e+WFO)ajELeV'VMOBגeI%ȸ"I$=YUw 9,HI9/t"D2إKzƹ%Mr\/7VUT^^F,oUV~F"%$P+yP.$rIq9S; 8g"*Zia4*O%O*Mm]8U%U5WV&^KߒT5 Ml$Ks/fFFFF5j5YcX5oᬍdk#ZVVVRK.E"J'=k:.({y~(sPUs*MTY<k#)f~+9a=3ْI$2_Q}EfF^MZZVk3Y5cXF5djUjUsTW硪S(M\$*YO*~EIV+yS>JLa:QPʫ%2Y$˙{5 DQKT=z$I')I')$r5O9S\eҩC(eVZV~JYeQᘕP--  :{'I$I$rI$zIiQiBE 'r$u2I...' җiyqyz/Eȹ"\A ܒI$I$I$IEēOL)L]4RGDFpARH!R|)$*ʫe$,QIqqq%aA*Jd"KIhRh/'^OO=?'~OKWzjKYOh4q|x +[_Qq5.$sI$GB*ڔQQMEȹ^r.Eȹ"\DQ"8fk?#ŞX|ʥUlN[aiڒrbHI,U!PaU%eA 3;B-^ )YOJ ?Cp-4 3LT %I$I$KCPxxxxeXbVT-7BMGٜ2i T'$Ԛ%s- RI$I$\\\$I9=$,EfiFc-dC7!Zd2 bZCr4ZYhE%#(%ȹr.\ѨJKz.DIqqqqqqqqq%ĒI$$C!f^s.d~,LɌy6TK{.eIˋ؄B $KI$互^BT].kffkƹPtkk#YF5QTj#P5 ˋ$LlF')$E%'ɥG?,*$H ᖳ) KAik-b=T[QmEʋ*4٦ 2ZB-vEZQ Qm$R[IB-EllI%Sj/&XP5MSUf5YFj5YSQ SQƫ5Yj3Pf5McT uZQ |*)E+ J<~ J~E輒KKH #j˙s$UE^jfjQըfF5*5NjQVj3QfFE캯%y/}^KnH~I/5jQe̽Ip'ty,M"ik!S?#ÓAy4Q4<5䰵HFԗj RV\b/zr ߫rP" iM3Lt2S%"!Ak,f4k!ę}E x.MU]IannnYYO~YeMɸ-IE:-r.B2Tr2Y',U*(\]I(u!؄ 2lҞ3HF&%". (cclര!$)#bQH,pll\4"K Q|#QyHuFV7]5*mFhu"MˉHA}%ȼ Qb / XK/dj6|UUWdH! !]h֬f![䚉#5yUy&'MlyYs%2__j\e&+)*Ǔ䧂G|1fcǕ#l| Sв} twN  !123Aqr "4Qas0B#PR@CbcSt`$D?jܮ**Ⱅr~G-- Xǚqk87,,o~ fe kfK9 [/o곐; [g곰,?XX^7kޱ7xX% 9v,/Y_zY_cg++*EtOEs Zwx ӹiZ|Ux?UwDX\ny7E6w7EwܳNY,hӖi4~k67޳CijCijcİ-_ ,F b~>?gp?sL6T9cN \gt's<%f߸!<2G;+2j2Y s&Vd,޹7eĬȡx3\ћՙ#7h뛴,Gr"#ZMET7Q<)3xAc8>#y?ޱۿu%ܱ7gx5gxB,9oKΝs\Vuǽb*-*Yf;`.n˛.lk 0ՙj5flX\sW^XƳ:>V|}ʫg ed.{#\@4qV5\X踬ܳO+4 ?³O۷+yXݽc(B֤]i\Y_-~2kDV"XS*{Mm(=`qLeɭI!ӹD]ltXI2u,=8at`JJ&#‹ ] d`$^&F.Ly&KJS%VFzI䗰=6/"pĝvif,毵Å~ t9Bã t- G5w&P>,Cz:95\e\55[\՞%Y\sfo\ݛbfY^k5 fyw] ¿W+&xVq[Š9!:{Lg+<ņޗZwQELY>þ+)GdKG̚5cuf!c:2(w֌ %F{B)KJMQˍ-_ {gz f|Itg\OdœgⲎֈy,s],)Ǖ196+9TZtU*mlG6Po5.BPj!1ei8Me/XnQIs ]X^ b\mgX[Vv׬ZJUPtg5 J=ׁhpUMZŀ8GezO(nv?vj+Ms?s?s?@D!?,dz 2k;RhQiBm;]V#U>*>nv?vҲ /|l ZItAz)w(NAPdUtC0!`/&J߽dn)ŒKf+V(tKlN @ pvwuN ~}SGfEj#uw`e_*mSu,eE  ѧ7b :,'J:]i9G 0lLc6h(; ,آG Ye]Fx~(ie}T /ZD]Ew(=( DsTxeҚ\&+]KdP*cA|ۅNEB &k (fiQFGڶ`k㟭 4 +_EuC|Tm>zI\!gXF jC^ޯWK'KYodDT-6;׌cd_FG|(v㟬AF{Ae]T=N~Qyh>*.Ѣ+haEX")X"~)Xb~!_B7 o_ƯEE|_WE_~!X_`ww?P <\I%E x^D-h(n3h{H>ѣ#>C>hQYOjT6OE9n?,hFu%g_Yu%+=V~'qW8+Fc~!\7cx1|es2_\/qW8+E"|e[j*CZ}Ƈ=Ehёh{3 C$k\h<>֨uuHN i&R# h!B 6C(Td{2ǚզ;ceTN6Q7e&McˍMhԘ6Q̄އErF:Q.*\{bik#@q u2E]&IN.Y!F[XrtthmhNЭtLe<\ Ķtt]!2ert9F[A7iB6Bћ.Q5ђhO~ѭuf;cGwɴݺe}DցSpMOqQ#p{IY%t*HНo+ yc $mg53 5d.U^ɺc^G"g+<>:L<"WDc 3daf ])DR@ܞ N^ 3NtXUZdL:0ps*:45 h&(;cE*(Uv~ܮ_ӽ~ Makr25}꜊s׶x6n҃=Sퟂv2ݏ`B*nM9?hUhqD93[Ѕg:J5nU.S ſAs[D֙_#Qe9J`mݥlz@e:nQ~Ε5NӓvO++[WQS蝮"*̎ҹ/ 8rO U>+eԮZwHr=I:NkD$8m}JAebG?G*M5\pjrG7 4#C``69}4@ JNƁ=2@.4Ο%}r~վ7׋Կ݉X;]Mhu7I 0uXu* ]=C@ C5.P\b" j !4_G#!f?Zz %ӸϩFB=7y<~ GJv[KT,1Iz^Y21:kESWB.v-aE^Q'U. <7 (fҁmḩQӾe>&=ԥFnվWhDOv +L TLJWC|k2H2fN%ȎZV\%bv_u i+&MaQz]8 ʵ3l MN í74ScauW47QvC֠mx6ǴfSe> \vϾW_ֹ7jTk+']DONG UFUW?<*=pބ!ҐYx܏0u4Ta!PVLfMɏ*zkOѷ̪&u޹U޿FhPшw"XyfFKUZdc;)7wŒcE}ӡ;LSrn՞SP;>#oaplJ\C'1ܞZUk톡^`$.]%R(#:8e䈜4:ֻr~j n%tF< Z_ܜAlf ;]&[a\:&a",.mc.*:rMP6'ݓ 3wŒtZ֙/j̊ʵ7׋5~V'v$>%nIrrq>#$~9, Rp sXVl& R{NaVÈq\RqE5t2MkP6'|gf2{-FWWy7jQFS=x~'b=F Qw-VL"W6:Y nLb_fDRQ>x:5ji0Th4B-kLSd2.#Gy04YP%lo%P Ǵ'QaeMҺ]ɇ(oe(`{gr", | qb EkLzzŭVJhNw-*"a612X{Nr'cHֺfRT o‡ۥ6YsG*4\+erIN6S3@ s N4ő2cpN9 .*2m A|o7a胰2alw)//:;f~aD}Gl5. G_i$|R%V]]%:仡Y;qU]Sl.3B# WX8hCVNƔ*S[)pi#m1ֽCuZκ+S 5&aj1Q]~(yh,lDѓkP>MSѓw`뭕kiN)}aJ{)U:{Vc;o_|([lrek<ӹ\zkCnZߏ쁆4=$M8V7T)ϹZaNJlL6f5Lkt'tjs@>Ʃ0 ԢN ʩ5DMua gD jz=E?]/PuҖބg)wR7#Mjk%[D]o}H궁^ >Zj7'zl6 LAC=Iۗ !8ulA6Y >uD+oN* O%8|Ӻ6:. ='MtdMVFOE᠂u߭8HZs]/wz.ɏ&qmM4On;/l2%VszJ%^ZbXY* Vl=e9A"bmF ={Xx09NrZ֌0Ft y~oʉ{ ",V8Ph>$.&BjQ`CYPu!='5E]evzighޅt}ilzE?}to*Z|Ғi6wu":(0DL}yޯ;ekNgBcV"g9HbN!3.QͪF[\|k@(Td{FVEw:ڢ'=n1%=# ޛ> }G>W)\Cmg.Sgޜ:m NyތAE2jV'"xGYUU,,K/6W( ՞YOt(sYبjޡmq}imEFG 2aJv]z[j?Z^&-k4}_|0UJMzyN& SLXisuSUZ;:ɬ!TBh [o.١z{Hj.2Ch=&'l*uƹP/&Ny\2uEtѡh܍6m QL[2W9)rzѺ5:גLC`:Cg7%ZNS،+PQe_||'%7+S]0Az54 "r"D.aZs YgBoXUh&YiD[6:а97TU%}hi4C֠Z> ]1Ue}Ԓލ}n(fzqcЯSh ]kXD jG>>Ql&+ qEkCH/U)#<'i_+#ćA)$c90٥s+w96R؏6c!;d˗  < ZJ>O{H[aD{}h3^ !C{+(Q46ܝʃ"~v{[}mQ:BzSNc"EFZ^!L m7h촔®+ӢTe{jCzPt9zdڴIҜmry mJ#A RC7XAe]\%xW Aڗ8Gl,T- B \;M9(5D1 hĸ$2/ ^8kg"QrxdN9CĈ$d/3hl| ȍp%isDDt/0̨ш{7M{^ɶ;QdH͈+VL';ΈJ<Oh5ijʻ3ĘnWQrޯW_L m4}v8- ) st)8vW.t̬Q0rpH*}+vp\垄poB/70CEW9э&&؋ VUge:CPLԲ̮%,+ ®W+V^UbX%bX+Gy>,ƍGh;nPዡFA`4G)qZ&JTCYM^څO&I%F'TN~te`XWz2)_e:P`WHYKc[޸Bs#rk/ FeJpTxz\0>^W% raW+[jwÍcG]2VqW+BtX8 "47iֶ!Eki*,g .,gNi5K(sk)+$B*ֈgb `*4YKyt2rtHtZ$ڶ"=zktZ]"FDR8{=P< . gBwؤ,+bC¹<+V~BY'WaYxU\˚Dܹo 摼sX_YV]8}(bl<.R /xF' /:SD(/- h{&Keb\qr]ˡ'c??<gruK;"J[; ,㷧W@I=)Aԡ4N-9[0ܳm?\`~\.e¹%l2OBx(!,y.sp\ՙg?Ylv Ve0;zĿXa"4͎4sH#x6*(ʖ8 #풟SѨ(c˜E՘y5$2ƻO8uH4j bl>f$oo do(zJЧ.$+5bX%^W ,abVN9D2:欋,pJ~5 4|Af],i4u4- Gz^W^kY`zV+J@|7;ZjE&uPLgxWdw[L)}Q:-!SXLNnM &CC`aw$t(OZUf Aԙ)Үr\VbX?5bW.'MOGXoRUb+XʱcXhWoXfo`+6zsrY%y><=ijޱ oWz^WEMrKa*fZ7]}`+ WZw;֝N?ur\)XUW'Orҫ@H}EyXݽgYz&~r;y,crnR$MZUįAխX+C޴+r\W+ABeCMAUz^z*д-BJ9iҤ+ǩfܰa׫V/WC҇$ '74t|Hvr^Ү+ X Vmffͅqta;Վ~kݽ^U^oV=&x&oWzZ7rM>r\W+wwԳ},*u7Su7+ʼb;"+XV"yZVi`XYͷrͷrͷr7r7r̵fZK6qZU W+į E7,+ V\Uir\r]Ļ 8+"ЁqV1aV5Z%;a+ XJ®W\W+U\_j5~G^7MXı,k85Y%X?%c+W{',NX9broW{ݽ^o^y/oZ7wz V-XZsw+r w,w,ܰaXV\4iZ}r°/Wj+g'Z\f`, XЯB+氬!a XBW{Zn_+ǒ_Ƴ8YҭVy\7)nW 7,>J%w\V+u- ++ڱ 7,~JϹbWz{;zӽ~\%/Wr8+q+J%bX$د xWxX%bX%^WWqt- Er8+]+JҴ+JҴW+ޯX%x^U›%ӥ^UzΞr°櫚4W+WyWX\\iuWWysU\sU^д+_Ekޯį޽^⽫V/u{ BS-;N妍+ **_fKGyVHղZw±ZV0nV/W-;iZV%Nnެ. J`+ͬ+7y7!hZı)毒ı\UjbR&- UwQg+׺e pnX/%Y³g_ܳY,nVqֱqOP=G)!1AQaq 0@?!"1D"  `$ABDTH,   hJ1#A MH-~X#05hF  w k#DD A%"H$AEH$AA$AAF AFxxO^ `u`F a0fCC `ƱD#U 0XA$A F AAA b?dS)>ؖ_?\ỡƲH hhhhhk@Cy&O%.b'5S}K#_'#7߰BIl"o䵞o[ÄwF8z;S߅GPV^T۱:Y7G؜ (5Vr<):SQ}~ X'~0VwE5k"Y%o{Og{ ''ˤS4)[2 ?{hhф2 >-vI‹R2/j߷kYx߾8`~G$Wz>ka14Ip&>%H."H:g॒V&M1 lV}ick${ 60Șz;bA#< G=}m} tp9a\>AGɒFgA8 .jhA""ϢcK5(@}CgHK&:=d >]R$hZ3'YGhwIn^Eq.+$u /=+*Ca}z VClJ444@ BD~ 3Lj(IJ:4YULEѸo;̖ĉ%Q+GbEi,!1-\2p04ߴ LFEBYظCH]KK%n4Jq΅hzF-"d|m&&Zl42 )$Fba3#,DFdIC'Zd$$Ce'4)EH!:KE- gdD o[n|lD+-J!(V)腁Vs.UgȊ ^k. j:B+D9t}ww!3<cR>c5Y-Q]#๞0I(41AC 0PiJal t L^[Yt)%hLȭJ=u#ͣN }4fVZjXCXo~.RIۉAXj܂Ey+O+X[%j2XgVbLE [51m\5HIU YRV*z%E!fAuRߡ쬷;KL<G[ZeJgW?^7fraGʋx. [0!f %Q` ذ%(1Lz9 %\qV}%܅Vd,E ~# +\F#\ȃtˏ"`%$7 8<`N,n fU|X)ZDzu– cDkD @d9WyIIM7nzX^2 >n4zB]0O˹fn@sN̜8O;!&̫$tYM, 1zAtD=3)XQRQuժ?WA˔ȊU@oA!v[)Uow J)o3+,$xNJg3b5E6E4=Q&)deMTr$+; 6gT#P+HlE : nRQ,e|Zz) bBu1V%{NS$0ĊtW^jr+JtA*ROqT<e4Vخ|QaSD [N]1=ؒ (U\KEb"O۔9vP?%zI3#{Lnz W=5Оyn]>պѺXHHC5gXƴ%WNwPXג&]!E"ea(IJnkAV:ET."몉: RtPl9XPmm 17"vr —'66Zi")cqRՅqL ԦVbs5MF0Eϼ|Iago M &(TW^}G~Djʑ@ d\Dp#I(DC= 1֨bOcAM#aa#g>D @t"D q)# $hb8ԸUb%2Y${+"/#4žE]Ua)*P, lxÙgDV㸑ĝ^i4 Vr5A$hhh␪Y`%PWFIy;+{'(=+?Oz}h_|C>>Gw[~za{_yLGs/O~5RKHO-}+CCI y?ow/5G7 RMd =,I05~=•4$VH1"Hˌ;x>&)O@ T(y\DyS(\ %dzWkw4. 3IR ?e|wB c}a\(o|Se*̗X$!W#H#<#U r H#@HuD_;oQ(XeN$r>#nay g5 -IER2ߢ&"-!2nETR9R)5ppg*TN ̘eэIHdErBU)Wk JdLl̲N *Gc6neE4ǣT?N4 BF,0]5wp[7XE $N$IDK qcG$pb8bOx;$Rv' $lx3?W0ޮaKHR6]} >ae<GFF5O+əW]Eg鴔r@6d. IQp7$WtSm"I$=X]&cHQ&(w BpnjH&Qqk Pk>Ȍ1Xy# Sr9ȫ U3q) 0HD7! `q|"ƢX[<=\A*BJDSѹQ&C!{[r&6 YkQԲ&n-62UhAm `Jb)ZU.҆ .7XAK UgK! SV̱K_xg+7RAkƒOY! hucCCW,(H{ȉ>R$`KE"0AFX"߉"3,`t huc'[!<69+U Qkjyڝ_y<"OљJjhg Zȯ!znQ+ae*PB[0M7^6}} ]?cHAQq!7"REߢW;>ki]M 8%QGVyhA<#36W?cPTP43DVcXoI$hIGDhL,K+7G Sx^J#\Z7NË"@)=fcIAR,:!a}QkK dأ[M:Uz T*qbP,E,OU-Bح+ZʼnK\-ܱQ\IJކAt&**U-Ռ%@,PH"u,-sz:XUIE^JL4 dP\B< M?XAIXeLģ"&/%EJlKlz!ЊZ2)G] K<( 96t(e˔"r6d^bjSyXfxZgp^$UbRTíL(10,uK1!N U ݉wERW1Х~ E=Bpc?wYt`?u,5 5A$Tg"DSa"jBHYP+g6L)@(<Ӂ8=FK$^VEL]!/[>MբGeoBvjf=fY,.33Dܢf5!O&99xJWYz+ HD=8=FfQDhhJpXM`҉Ei{gan}{=C׏h'Df; )U^#8܊ Ase KŌNCQ|̇8f^C BNMU2P3Ыd0[B>d@hWUQm,HqUԁyjZ\(;h,VCe Mދ[4$kbY-xWIcc…U՗oJ~QJ WmWQp `h,6]#Q F6۠ov/̦Q SfX4E I ,4BȂ@Ԋb,Y"uY7 -ccᆤzQumk$wTbQwqbXf _3/p|=+{o߳߿d>oCʫ=6y~q=y#{pAhB6"GzN <'i$r̅ĥuO[ԉzUH"5GAgX>HG\HE8&$!hXoI DcAdTrc2!nbOcZ_TLYZEmŜii|{hZ$gOzDSP钕8jVnYrl￁h*B? M4,3AS(5LK15ƱEUރ*vI i[8fEa¾p. iĪ8iI($\˧Bmz3ivQ yYm_%muRRIs樼^QWԨkVXӓR=&=Vm2WW HX!z*MЊF 53\a*'hdR&#Mdʛȯ>p^+p{B JK1 FB!oQD%?+q2"ˡ, /*o[tt.^bi8C ٛtUg,TCoIxe{,EcH[Τ8H:e2uJmdWRft*MRn¡SRjP1W0pfу$]d'#9Y'IZaxJkOšIkұ^Źէ8ai5NBUKukJEsb6Ra:hJGُR+*z3=ʾ\i0"HBB=Vn9LIU! X5uշ~DەO^Ƚ: [?$HD :"_>#Ar%m (.Yf,PB0* 1ٓ9H𠺹@62sqK×pA[5ddK),ةiSB([^Tsp%y*Cʩib}ބbOzͷg%ms/VPVbH&IS1R ( =mÊۻ4Jl'*Hז]4 t6қؓ$NBEIHuf6& 碔CRJz )wY~P8|彵ė[w@֩m^\ ++A0 / TxB_Lj[_jK-o}ٯTUNyQpK U8/ض~LGٗ sșe"k>G"WhALK{=Pp+A]B % L( ?q>QJ{TT?PO\?u3=#{,  W D/ЖNEb$u_+R,Y*ӂvQ] G' K/QW2$W/b9 ؝#bv% K>j& Wd(GrǔK2QO&ªa)[PJo\c"ҦkdB4RteB3MR)M{%+JH'*cSU+JSԺN'=/^ዓ"2+GG/S QDa=kscr5 5*Ts nl)o#Nde"L{/U#PHHTEͳ/} ADHEs(Hdh$sBGFL#2jVBe#z{m>lN]_ñ]&ڊBUW> P= 1MYܠD$RNIͦrnz؞vIJglZ7k&SАΊƪ䨐dI} ̔YBr!xB̬‚F|&ʅ̭,şXQDq(nSڂ2Xw:IgA0 $MDg2"va2ّ @OW~/EUV9_TDsN|r<3_Hka-Pjp?|`Q˗#"$"3,(*JZh\r$8dVJM:~ -.3XM T˅3=VYH¨T(k2;U2/RPAO h\eZsMsdչ6g'ҔI\LW(ى(?,| BY4NFbf|R!Rf`@7 RA,tq~Q /PS}XM9ŋ.nn3JkޥMhWk w*aTrCR)Rg< DiI64~=1fFBR5&y0Іr:n<&^ؔǏR3K 98˛yg4{VB.@ jujؐeGV^{_)i3e>U+$ҬmT#$Нle1 DC69/3^Fma) BҡZJlTS[sM*I&JnXע-a R'+*^.ŷJ . /{mˇc HBz~Y{OЦk5V#~5QILɟK5^gqBd=E1!EХd7 E9^ P ͅŨ6GyiHDdE7*!g^夂EEbkqS8;bNGG1nj)N%4qp1ui ~?NWF&ISYR: L%!]:I,d+f^˰X<Т + =`D#Z wUЮG7KH_n$Q|g5.24ƳRT g[ G Vs(̊1f̅dd;q8I4(&sE.3ٔ%"ʎ(%\ȡQ++Q-ayИJTf{ 4S?Qy}yFfZ5B XM k:7NZ5;nA*22My*_AG)^M%;t$@*I*gQ6)PCzD؎.?o2H}l/+c=ƥvX<A?: WrX\:3u&efU$ydRUGF]'A>s|45V9./ɔxP"$hM^U)STH-P%_JWTi~W$ܭ-N8YF-*iiOlFh0Be(L ͎Hd.FX.BR(> Rɦ:Q1n=siq@O+A,|91MPRB|6&Ղ|iUW+Dӑ-P,)IN 3[) YLZhvZ>U98KW"TX\źWhs 7W+1wQ7r3+Вtm -Ii8nw9K 1SSy$ޭp:}GDR'lxRT WRQP-C[6CjP#[Ec>yXwjxX%4x?^Bt(`?g `Oţfu i}XE1G@$fy/^uڙ~ӳ4yh&iY4J"_`OZt_^߯vm" I(Q5&y!5hĖe"*U 2XTJFBp46_!UlFl(FcD-Ԝ+hђdM9SxdQ8-"t!46a 6-Ea4/谂:B&mRQj(c.ozIiRrr&Ѳq AXtDr.xS$7Nv8Vlj[MuMxD4zٵ&Pnxs[`͟XzQ= d׾%D>nq֕1.RD*B«EH4+}Dt57 mA]%?Bl1Dw*E+IHj4= %jJԆC)8k% ረinY$D6 hjr VS'ݫ^G}AVoUwJЪlISrGxŔ |8٣pSuQ@}jvUN.7dMB(2M7jɽl:^Vpgpqr?W^u.W "":NƮJ䗬.'A9S4¯+`PLҖUX9J2uP-Usoe@rÂN T{̖!k/z%,+vljCHO-+11LܧDFVJ!*O HP֔bUUn3sR]ԯ?("I3[^ #wPamABVe\[3Dp?>eq'Q$-$-օv?86͸8#B`Vdu#Zꈄ").{; l Uƣ|K.ĖXmmhMJICZH}4U֋FRچ*6I7U.u#a](Hlz Ju21U| LhѺ ٚ*h[qgiZ%"4+Fth٢υCZ!1HȦ[aB)T͊qffj+h[8HQ1Q,,NE̥hΡk2:2E!5FbbFǐѕ5K3XE̦uZub]`&r:y795+tE2iE4'rI($KK$qb 2v` e hjq!2tm]RqkE;XIEwFfy'$=ђStA!2PA!CV'"\Ћc NK LЪ;A}1DXn "R1jKnbiD+8kO@ɥd̈́ >0哬8}9f(s?($˄_"Hj@'KRI$'HN `Ĉ"AH!T2=ϸ2Őm㋿Q$a\}27A="/;)r!z[͸⡺r!PV~';%БRe,ئy"_d%W M5dd4YQ(Y5)8bUֵPZYimpn7my]t&d17`o7WVyƿk?#ThҚ1%3FmW".=mZhBRKc lQ +;意_D"_|^tY-?.M }PŀOSA;7O5$oleىiMىK 6BibJ#mP/306nO7|! XQ`iAA/=hS#X$F,#ܓ,/AH [chc4=*I2B%% 1^2¦/'-7آ<} Oբ6tFLP# ";p6I&^ZK#mG"7Hq'gcz̰b6&&9uL=lAq-ihl3cZ2[ђЖb+$I" ǒ-AtLh$wFa1#oO`l@8u~hF6fR5f.㒊WS=-ԘbzsȋL,Wix+(/D_IdĂ Iz?G2(@S2oͯc)?V62o9Y013 ˹0lh]WÆq{_=MNe8'#i%$GhaO2[Y+5ċzChM)q$ @^zƛ1M aXuGHn,# FE,*El$@4`ZdKRT6)!ɆhVBA:zDCxUФ5^=G/ZYf&XObp6}qĜgm'QЛ.NP04C,Hy3Uٳ-/#{u3~je3w ߐmhbr EfCd6ɥd2$ ! (,݈ Y2am^Ncq--aO淂WaUa(\j]I+"An$]Sc %OmH%1=R0JZS.0SHJZ jt&)Vkq2% [4ƛ܌))`Z\?RNb~Ķhg Ci{Gcu= {.Ub ׉CEf8r)oS@*wWO#&NZ| [ .!)S } QB* KO^ckcF~&mcwu*Rv2^:83U(,ep-H ΃:,j%M·*QY;4z~I)RmCzUE]qE 5g=M+%7Bvٸ(<-|XM˄ܡQ֮!;Q EDo.PN]q= t=`)ErdFh&Ւa2r;Pm8m$Vd@ >H?`Lqj_;ϗ!Zg?qYy 2[(MF`WCLuᛮv*[B0&ާb=8;lMpi1F6n=novf%>JzV *5GQGgg^⎓, 9TY EY2Ƒxp3 $*ܐ#v#\$fB5HVƮ;;H5:@O O+8h!:WoPymGU؇v2l2`jV(p3cqyr<$Yq/H=HCStIOUjs]ķ`Ք!^LNG`  ;"K gЕ!9v&ӱIdsW$:jtO6Ib ƶB2ddCtWXSMeUk@dLuMep$["W Yri0K6hg<_qse:1kWk|;9pr):@[oUj%o(l"G5D콥|&rĒsˑܛ9fb[Y YzQF~P? GJ"p- 1*ʣ[ M)VG#sS#[SwOQAg)sepUh1BM~ȝI2p?@U˄G!KU'>B:U4hʅZ""p2j9,4&TXYW"&jH8)w'TKL׀#Uf}DiE J8<߀ggºS舑((LQ?*y8$o!Ovࡾb.6L꤉SF"Ci>f쵩vLrB M XF#B.; eB!DM 9; *fnt!c2{1&e"7dt `/>wY[C<o~Pg  Ne2nX#$$PEj}v)[ڒX$D$LI &Kxp I믷ɠa9>@$ @(@ ͞?|0c?*=}̀h?ڞ{)h S!6[iC D|gu)1?L۷5ARGI4Z i$Il9,8BϫaR>2ExY Y)m$M,+0O+1進7ߵh=m&Aј%G{ S$IRԋh);Oh$H@Y40)I3 T^F ,-B]AIK!%"IA u.{Ik$NLv?iu"OR  j@=.Z{&-c& gE%6_e?mk8oA_3I$it͎*]yMEK-Ynm[tWv7GלkjI"VcbkihH$6M$eK,mA$'gfgHK-!9@ MIY,,0Y{MP0i0)f0fSlɽ iI-ym% h^" [BZ25 e5дM&m&[kJak96`*Y:p[$["M7]{=iEVGPJPRlD״H$񭶓Y><{!Fe,w$)Uؒ-ӬH' [}[d%jH'4EA(=myAD 6sy}K}ϬY` ki8:aX!8$; -nyl^ɱ;y~G7Dn2ddI*EI%h2X q=(}]ŠS㥦=풓'Z@$M5[/{yRyܶJ6(8,zºv9h+Y^74˵ ndK!y ,-|H);7=Z̶G[Twb$Ȼ4C!Q\Ŧkv)K: em]Kl/ =nHL0)Y tI͢oqI*{;p[mb'<g/LJ!O`"%鵀@@% ߞ*>%xKMI@Dȕ'0{u߽JDwm9k͚$$fH= 5ح7b<ݼ'PH$g}MoInɤa$eT?| El,?ZH רt~M&]4I n yU6B&ܥ$~d߼=!!_PIH-=00i qnv4Hѿ˅hwYpGL@z(!$pd>I((M.YO|U]^ot`y[]c[z6H$ $I @Sny4u=C;~}AqV%-8{|U [Hړg XT !67VrrbB1=~"H͍9֛<9sH5 a[ffAyeC/݌7XV {I&.0 gRH2Y'i6Cƺxlp2S:iVEȦ)<4B-'ǸZ}%" Bܵ=qd8e*y:sB]I" W*26Nb~_E;cSGAs j%; oTLv>wFN&u F6 $؊an%0¦GAȧ䋕$G3"с`6hfJW'CSHL1Kƈq8 94fY,\:KkmΜ43̋kZx&h9Md` hcFJ0"DbaAbULËR !P:to\ñ{Nj.*zFxHܗg[o@鈚gҵ4tt!d~RUY0O To3Cz5".,Hk-Hbw(1!A Qa0q@P?Ĥj A*AA.ƨ̜Nr6%z(c켍6t ,%Hj2݈bCHXVD1*% ȅ#CؔgxJ Of \ g^ yl?X2xe XYNebp|*L%? $; Fa<3E7/$Ebl~lX㣌,11 7FT.i&~axA?Vhs&%s-*I.j4XxYƈ"8^/(ؑ?JfPN> OZX8O_ ذ<- bKTt,QBr>r'PbĿ^% I<Pİ6>0زQsؕ4Q/R#αL/ǏA1I7>I?9 ~¼Q&%^n= XP$)M\?Ŕ\=fY^e~wáf"()JBe)JrT2L,ׅ.w)Rp5 :;^ʗe[ Wةa6$}v}'b !yQEx;]=ߘ#ǂ<#b)kO2{*f7HT_EDQ2 B о/?=HRA F`Sl(EV6"QE aD A2(┥*)Q~ywL/♼Bdp\ M>JB#ЗRNE{E^&WFzľ `D ƫd`*s7L@m6iG>6&ziǙ&.H4n'RoxTЯ෋<Ox$ا#m p 4C˂\5r9 !#sXֆ :Fh4GȇG!z-n>GȂ$#W| ic#)|/s}}|X(g@||0|v~||z>gm!Aq{SZ-SzxEvG[L/‰kbup,pC/:P"^N zQEdc#ؒvLo%v907g"X$7_=2?0A>W7!(%1!ODDCJOũB7dKB>%:c|!/7FX♽ nK F:'; ycr&=hXؒU"q_/gȟd"4{xtVVЯXoL3o'ǿ9%stah&ʋߋ`Cp9X$$; 6,5J6/ך)<k piCE(Z-'Ⳡ?G #+ȚƉO5% bS6>Wv$54Є!33<#_M·h(kZ(=YfI% ix?61>5b=a b { ~u_ÓXKj=iQ5O‹/1oV,gb׏8'{xׂh{k~w+X3o X12\&}Ϲ>s}Ϲ>s_}ϹlL-Y0eI&ZƣТ~7-x>DDDDDDG"""=AM xzʅwkyX xOV/ v} E1v6|EM cĩ>gA㔐TH!?׊PXJ14зxز𼙯+=+! c%H*4QZ#!̩}hƤƿA^7-A G1bP|EC5CBK>h ^+yȄcQc_ro.3yǝs&ƣp?#! ĭ#/P&h~  '%WŅ1c.6R?mBRD>}!Yt/C1!/K_芭ƦpOc·GʢpO6/XׂK񰜼̮(~"3lNLP P\1Iv(ZA,,eF~Wa~FaґC."TWȿ ĕۅOdBfZ:mOAbbT<¬?+99wƊS~[%6NFaLjH\DDT98E6Bxb~/:~0 H'z.6G ^ƴ>b;bI"hbMcsZ /%f7aJrף4]GhdP[d*P)'hb5fM0!0|h7Et.Tki梯 ^rHx65I!D4hb"Li~,1vL H1aBq 2zbssLkYcs/׃_?'l!P\?9di[ ga]5b2iN1e{%eG9 J#b;׃W_/ˆQ?]?~Կ/X[ [Ĥr aT~'p2We oGȅ$t/^oGKFliN'|GSakʼW׊cVKk86"j _+!LO!GTNx@ʙ2XfTSߡkU)MY%bm`nyxE'8174^K**! TTTph8diudDDHL? B1p4axkYyشr'^maF8z:(H!Fd++eeeee.+)XNapNR #aM2zb*i+rB6B=| pK HHuXweI$+нȏ~4$l)g1Ĥ~LECeD$CH$2μCQEQE(HƦ93QKQ:1 -<%ׂkTq{ÓS\pD(R*v ٷDb'"NN 1APGX>"KjaNh_&!1B ĘVDũ0I \cBbf-k|1(RLwF'@'V'#QK"4QE.Ck;)jbI<=e(4|"XO!<bHh^_ = 9hnˑi Q q]"HFB ||8p4q"f $%a÷,Dddddbb#(>QE B<1.qNSt)qJRR+Dh( IapHC$ Vrخ9{c]curÀC5v?ϸ솏a|0(*]5m {L7[A iK vS/غ"'{#2}""XcTT|aQJ-?+!X>#|+^؈}膂 _ !gzIaY7$6bf` $U*2J -ܴeyw/xV_cK}ĹqxrGga[6 etdЦ~TNRvh7fﲊ&])IXKg_ck} a{eBh3';qJj675ϱ~]]Y6rnƞ衕Zzr>#W7#\"y':4FrOcEHzGhN!-З"= cL\#ܛcnN'-$$jD hQ~?A>* {9#dGXbG_$ꏲ81Bj-Е /.iL1Ϩ4~CN5z/~ r"8F0\ ;>XG%ȹ`GcaA:1AJp\)V7FtEn[g: >B=r䂢0T$D%//Y"}^ދ}ROl>x~gn= &t7G:)S+MCW5CDЈ$h0^;e_"Wה?8fI9tDR+^ϡKO)A4C$ VA )нc8=!ҏ#jȗá2)g/Z(L>/{?d.ƾĈR5"FA#Q`ig2P„t%slXe{l *"A'h_Mȉp? ]bl$Z?+vײbgH[~; /I:>"GC?}hX{6X_x6 ؘLbh'4ݞb˄^C+b^nR=ƻ|4M О*= Ejc|OGJ~|Xp}?!٨xAuGV;v$"DH H&E9e}#8{###9f/ H?DHrGuLȽ'} i M貺CbXhgЯxYmO6.E< <}D&C*O_D"DLV5$F($+i{: ќzMd/A1-!aGD\#Bl|lLҢi=Mmd1| *YҜ NQ3.h*'">{v15GVs@|P;WV+ h'V$b0S@ M(7]cL{?2q^ ܦ#c~%5oНc?bk$v!gAl~fbB$6CMT*#xR\턭8/7.#G |"t; z ΋$;ll1hf&=aZ`3 B5F+^CC-䇱ca{ñhae [=K'B/c{YІu=/*1!A Qa0q@P?_n6+-5'ع:ybBTdBxo Ớ?>qKJ[D5X!񣁈b?үgf1`)3,1.$<2Ɩy!*q KH?4Zu笵<7x5MlbxO/ X/+5~_^]/YׁL4Bfane/6VJk^ x_*sH7\?7|R!3 LB ! DA?bpf/BD#####!BQ(&| ?LP^),g쨴.^Dk1$VRlS(f!FRbgMsCXeEgLwн tDsGit$ފ#nD.YCSpibmE>K]ۃt)Nq KfXGeDz#flF5 1e>ưkb&⇜Z-P&BA.MȓpЧA"ˤex6 $%ʣfCQ?(&Ռ 8 5EEЕ=Fsb68v,m2 m8)JRq c(L̂6\LzI2ҚƎn6A84l@ߢjt1~+ (Дb,iŲ=dGa90uhMP_3UЪ'UJU)4hkd1vi8i)]F6aێ(Q:ZB''!#dcLpWL J8E jCqȤxԮ5_ñ $WrLJЇ#9KCzBo #UVPVP_EFI6Mu ͸[eDԧMX5nBRD# p}5}><6! 2. z*TTTU訨#tP٬+cǝr53?DŽO}~ϩ>S}(cobeyGXଥ(*-JR+bmh5[MV^?,|^ 8npz2C=7cC?3|ρd T}QG>cg'ļ-.E!(4g>}q/.q L6,lv-XezU^Ӈ+c1b\h~0dbY'u4X,lń<+X?f}0LHdOlIpWz:qF |b7c~q"IVA$hB Bax,t1Ct\<=e6: Š)b6dCA>KbprgHnNQp9h (VNǃQ;6˩דK/Ŭ=Ӽqr%5ゆg]"kdȷO#{G>|bIhefj$w7?Pk{ɛ<,Pͨm 󳱠 MhM$8.5h[cqCy[[x1h}ಎ&GN!6E4pֈP,uZqձk3rrA,,JO!%<$#TwEãw' yAbLt8GcX{gCnXFdۅcs& N 7ȝ-x)%Ta+{b6#L7=b10 tmO ux6^brCtO&5Dt[5GNjFhjtYEVjY /Šlkt^z: ?"TTI%52'4j࿧~,Z7,l,##GG=z"EIC\/(|R E+%* U)L^pǝ ߊ X@Ib"r-y FCob\^%9 ',I,=pA6bacE)-F ^0A"a(1}. 2c%-seF5xbp)HB E &,mr!oriJjL%?4kb',0%Fᬢ G.sߡtpxdMe+M35˘RoCQv?'ٹ" x~Ii4^-#XE!6ؙRu^reUƸw??8FdQ*EOL#xJA(LZ?YXO1 >I&9(..|7ЍōNnh.?μ^W?4/~3>ţxm {t̩$UTOLK D1?>pvm~ aJX\كACu !~)r%Y|'F*pu'{SEdlJ#B$ e6XO7 yєXأrlg iQ$pO0(QX%HlÑ6pemᷡ8'|/0n tbS+4?>cV.l{&s"m(k=l 8| ^<o RbHŤhWDdcLNH粶++eey&R#DTpDD""pi o.Y$=2 $Ҙ{"# [[e(!k ෗屈crQ5Q R _B Bʼn/=H\l4[șH6htJ%_F:k_,jF :r)؆tdD OƗ}UE<7 iNWbcjq@n6;I#r!Si{*}v}G=[JlNA$Z$ob^K)y/=?eŢ)qJUE cLpUaiQP}GG&lnExBAh{C9\"aMtcU+&G BWlCc]̔l7jCe>؟b S zπSBD |IO}=^&v}鉦&0+9XVxUM.Neg>ƌhED.+bD5ȜĢ7j ˔'&r1-kGdn>Dඇ1BF5ʲ(YsJR ¶&)((ce{/+.ϩS.n'̆e^GOjt֋5k$FB_ez mt3!A]i WL4*6Q0&AS71( %+e^èw=ؕ6]w S\ \QJ+)J>6'VQ{QE:(߲ \-8(|1$Ğk؍W25$p@hU)K 'FR 1F|xqfliQXk"G&Ä Xح͈DҔPfQ^&_L W#炾 ATHv:G)>BE -І_p"j=`t`EQq##b12cM?DO@z' +}ϩ>^}3LzFmB G](Kd6qxDDEPM7Ҭ(Jb'cC6(E 8ltPDZA(\|]"/Dv ?>qq]Qbz8!ۢLcQE^(k^SlإSN"8Iƕ! !FF6a*'g؁;|[8$Bfs?BӧXǡ>ĞϠBA*$J2ae؁'!,$}1}Б_*pƚ|C'G)h2i1b4 Xn9i] zՐh&:60^7b;C[L.B%? D%H!Gbv$v!Qa c%vGWb?xSFtaP8&`X('=ݟh`M;P5Ma.EC"b pr8p2hjRY)Ļ1&))h'ؽE{;=LJg yS$O?`J B5N'BBbr,`ПhнXQxؽX ݒb_bƃOd>Ehb?!(>OhkCaa 6Af)9 Ũա!V481OhorCG,зR"n$%Go 6|+f M ~ #/q>bGbyϳgNX$uͱ;+>>ԗ4 X>6_1i6!J@B Я KlD>8v?h ݋ZMܿeS]F0FGcg!U 2]C%blH/TؑE =2 [9c8J8D F6ވ]!Xۇ`LB'_D|2 i(4\Q/Dt;-"D:( $tr1G a'кhz]TC5Z[|a} h7l^֘įLOOmXM""svčHV> > KQhe+b!gز^[ l;tS쯶7Cm >ģPmleUnpZ'vWlR6"bl㛄|z8}WfPݴ',!Z#tCϖ'Lu8lt'OH3a!.#7-t>*(d;sS Wdl^$sն9L}G(qp^(9,l9D2v999hF^oc7nHOcd}c_e VVۃS/f9[0gAZ_vt_I>F]h [H8rqt>?`MrC9.v=8c>O!2aCD;)Bj *B䐙.PaХM&8Ih"ýj4čzDnѵ#M N Mщ&Djpi.Ƚd{!(jDG4t"qQWd2=2yLzRbkIa_:`$:\!*qz9xAP] )b{edVVs\l,I')]wH|7 tlGMv&: ?اQ(9.P\ jau_}QFB&46fi nl3G}Ťt=b.ǡhzC΃ٰ0F35FVų=v;674AffضѠšlzٱvibB!'!1AQaq ? M!nӹ^3n ĭ!b Al㙴+Y)cl0]ɗ$%5C,BxC(1Lg,r'&%,"?ia&@{f5Q:Q&U&3hk^̥f=Ff#S56^5zœ2:)f@|Dq0T[qbf  5H홾a9VPv&buX01\У[hAn odJ7 AhKTr̊X%G"P:) AV G2dJ 0n!XfC"xU~y{q+%Ӽi.]gjNcYڍK4LFE]LmK_U,!jé|kdX1n2G᪄C'@ rDRDKħ>^9yǙa P17{3eDJXyұ)v(g)pPC8cojxƬ#/OF:c,̷kEd&(3LfJ NQ9Xq8%J@_l ® A= WxJV[*RJ1):[[L%Y-$VaJP5K0+y/ [0 jrPzL3$f?$oGa ng PL08QyRL s8X)JWuTi LxO-ظq&L,fѩe\K;D)0AAx`@.&d76:Jd5]ʸ`n-K0Alw:Ԣz*fvT]6JJdc[ $380MR>"Yas)(el%$_3N!N& bMe2W;jf_du55l&D K1k(2md;c%?% ]AevSfp"u6`%f$U:w`l uF Ĺ((! ^Pù8&a)J ̲) J9_SY&rvO\Ȯc'囨׉>'V7c#-N#T!*'-"pF] lfjvc|ƅJ.&IK.ΥcK%f zA*TwXQ5m%(aaR@0&P!\sB.VIW4@ !IU ˼ He P,!p1Kêa+ ?޳h6qeQa[ħe1_rJ8fs#*e @J4GT7 FWQ[nbHfWa,rb쩹#@bBG00p;@`jxG!Pn\UBanVf B : !!@x1sԞ)ߠGKC#G4?#El,TpE$hb?+:ޠ*g r3W xY:~a8LVjۡD'+bBO/.3G+ٿWLY|@0C_`z%|Kʋ8#eFotĦ2FދٰX[A]](zNg>XPQ1 D`Ԩ+ÏY5^AO"dYY\5XZߘ?-kŦOtm:7`~!!9N?`C*iPXBi xpC&-xX+>dﮃ)@q>Qo̎r[zTiwХD̾QX 8'g?a:zp~PYp=,!Ș>5D4 p851YuG"kؘy=]N_իGoMwW{yia5TCD:Z@鶦0?Ln'BXqX>#N_墨M+]U&M)3?t0OQ#0re-tcЈQeݖh$o.U ^i,='pQu@@>Y :B" ̙H uY]8JT'@ gƵPOTa'+Jc kKT9>0U*g b3^RDz: ҏyHz11t &oڈjeVսYɖYJ2R*e,3W*U[!7!`l miF2"IY-ZV1YU]мi`jNKw&^b5u1HcikSs>8;b*1|J XT, .n]]u7R R-ZM:G%W{C 3FS;,T 2.ܥ[K\&U`!#Ų5~ݑn4)Ws;?7VOrS72vS\hKWe{O ? QP_ZSn]Ӿ+**{fcz**&9=H055t(^-(X sE J1PE1oDIe~ H-HPs=&sE/ψ`Aݏ"8Ke\Y5n =k2.:+snB\{zBȋIA`%&%\5S1*lY`aML5-LJKi$zhr!ޱN7z_wFiKZX>Kvb]rJk dP5Kns;5< vmo8|nPoe 2iuLm.@*{ȣze:v~ 2!\/XǷZ7 i@:Q%u/:G$X+g:OI.OWRiD zKu0mمSF0aL 4"bo`TL=.=Vm_B֡q 1tR~0U^%( n/u)^*&:O,*M' ,!W]k~QɮrhQ8N+ 襶\#Zuavf3$s ^nJ几`Cso;k/hZa!F%AP -55mj9ypVcz~3cK+GA-hxo)^ȏ>1!/Z NdM wi uZ kT ߵnȫz7ڠ$&:c%փ_6\Y4_2.IEa&ؔ7It5N2͸+*s]& am ØNdaOy_TLcsQ59wqhm7FR & UA~Շ$T5st-'+ /ޯUU w*'ED%*Vˢltbuw8a'8M.[eiAG`!1? wr7GA"6t ɑϚ)LLc@Z;X-/Xf[8ҷ{d\g{A?SG"tG/ }6eApx=ĉ$@v@cd#_JzˌZk3V4' ¹C#gwyP3)'#_$o@mۥ/waHj_dFZ!Gu*0y̧ fNL+-|#Xщ(?g?"자w0}8CvRV`v0<^zƊb owbPtO]St$8Lc&N?2ɑFq$7EYJT(G/%P4lG8mh@Q\Z( TqԼkYv"^LV~a o(1tw+ djX%#:0Ncj.Tt3H r;-1:qy?qa~f>_/#>ӡbՁp)6O*EzCyMG+'W2KFKW*#` DԿMf_oܓ JǁA7ȥq#_!9eARE<`HfD;I3*2#/]xO?.+BZ.C`{OvSGk8tR HC]/ Dn+PfD~s;&T^?ؓJhnE] _!u3ڵIҬq PlZECEUM ce%dhWA_, Av9y247x !=Sp|wJҞ&:Ro.XvZj2Yѫ/K"6, qxZ^6$`g1:3bzKQJeD5<SCs5 )i9T|M e;Coḭ?u+Ȣ69 ;aPaI,90cZl(,Gd )(3*kr%2J)K`vLg̶`aDFPSNrǤh z1 #v& GztFFw. f8N#YJ!`#Ɋr;ߘi"G1$ ~M0q44Yg

H`UB/%=`t]/?hlKoIpΰfv`V3vlD dmu S dK8r@JK1 =3`zѭVos;]JwZPhLOsXȥX+3=-K?l>_%TR 0 Knq[q4JT{LFF܄Lj] *ܜDR\du81lαuEj:9VeUc iC=eXf![ ̫k#{N;ѣ2DJ%Bd-Vnz*#pp\@\wܹ^;(jYiya{|wSup0T9Mģ+$?{iE-{ư*o&Ot43W+N&j+[!H $'?į*&`^r *r=6+ՔR6Jwiff][rFYŵ!i i ;_{C{E51_{'TX#u.#\@q؋_K^k(&|V{eZ.i.,,+7r̢YƱZH:\kPBy4YIDys_0 %aN f]$krh9DpE-bӔe) Pe H"4&"oD"T&fKW (} ]˳2 9LIU֔ KƲح] FLD0šOK"srkL<}ߘ*Fi^Tn[G6L_Ybհ@_ ҇1aF9tFiF{Ua1ң% m3Ϙwn$qTrXu qAb,_ +LVw*|QfM"QܴǬT)b4{MA.Дz x1*!0}e0dfw&׃e[/|K P'b VN19W״sl9:ܻcm˃} L)!!B+N8umhآ] 7l#,\UaD '̲lHDMܻt_QkLa&=-|bnRzD{^snĴJ?i國FA,.кΩ~å?%66iu5P`C.qIj6{ O|8@EtUqLjH*&J;2u\sy0FJT.%Jҕ1Aq)uk>Ysew [爡>=ߟۊdfD̃WMJcNXhY桜aQsA<^5Av :%}:L¢9i,FR`rb.P1-3*~`^pa,6KQ$Qu+GѾ_(j3O&q'T0eѮTĘz¸&&%Hy_kSd9"Y v4. Y,#ZlJ]VUx,w`08N-ߡށ=L}1yoB|߭KQןW_Z&qӼYUMtp:ׂFZ1\cʩ\o287y=jo`@U1 (ni/ X kRlwZ{0Sߵ~"*:_0Yf{`F:WQUm'7 U}D"S%A@ w(9 zLA?357W29bJo1r,D MK  [Q](p׬68mطz//B8HJLH}SGƝjRjK{^(fI i";cc>D Two;Ә狷M# x.RMA{SoDy?;̑KVRd\Υ ?ȳהͲOGEj M 0gO.ӫk%;{~_ 98nKy_[׿DZUwsqtVtxSqZ^_e]qit}c;?zJe;?RyY:Wo_̸ږ8Wuv׽uһ!x~aj<q\_=u 1{X*TFW!ya;~AN ^ 0 .\U9uP:3 N}VU"4s xQJ3+:f~J+p(CVGLmGڀ!mqd+ Q9P3Pv&) CWuxSgnu~?=H/h@DHGw1wA@ps.p2iZ<~}56!Įřւ%2e{P?0v+iq:Kp 6d(+GcOy[.NYqQÞE[ zc!CQ}RQTƊ}LD OOsuaNo~H6@S\]IH;ՆX|/"Z~a OoXet:η?q`m{I|qxtVqSe,b*>~X^q {"nB}*1B^-8(Ӎ<3_~~䥾o 15?Q,ڿ_F1{ثW_=TA| [eYx5É\+pEp`ȩP%Xj հgPmPu]p e^[Ui|*+~UY VJWEjP\dmh`ۖWte,V =Iν.W :z/nɛ* @XSٚI*Rs%ږ/1ܓ`LSܪxSaYޝe*ip Fc dߠ#ka&}n,^[H)imUEn1 LmKję}Kr<8;rَ\bv+Q|COyVAc-h71S7mDžeeX\8Tj~Ziqԥ@/`Lj E84m1|gn&bŕɪ_`o/d\:{@r0L[Mu?ଞ15UXk*+ϼV ĦqDeo9`gw0C>bQN}7>?s/8kqrj08} u{O3`*ôJgߙJ˛@X¦>jdZr D[0M.\9ôU{ݼ _gaSqiW'V;ʠA`%W)Onܼ:_- & `U!JKh-Y+ XZ3)QI wVpqx/B):;BJ Zmh7IAqtTjUbU{ F#3+oB!ub{VuVbѧaGz`#^l] Ķb[|q 먾 YZc+W4]D{\Kh)YVRxn Vhqz »SRP(@2Ra&>wD3!9NY&l2J. `2{)Úeɼ]EM%;am`r<@^&O*)ٽY.f;?o}-r=]›[WQH+e| ?š"x*{J`)џ(?s=~*2Ud[}RR_Xr=Q͛qY^SOI`sڸsC7Qk~Jƀ{)[wq^ gX@p'Yzy™g+osJqԺX#Kc"HcJ^+q17jp-8?&+8Aujünf1{G^Qlfk}M}OVpP~OXa(k']87)i-)Q3cumٍv L^,kS`6$&KE#@yF-_- zܹˑ"(1GU#҄mj-; xZ{uh-L=ʛ&JN"VNٿ,GYSz*N[h/Ruh-T4z`}!-@fz⮳Qz:[rTNx]llǸV%,HW \?Pw?$|pk-IIo>6ʳ Ԫ_~YshU3!pAv'X  ~Yվ`ĩ /,8X_Iv6k-=fșJ>3Av?m̱r^5\X8{}Q`Ӯ RV-Xdv,zVxz97~ hCz|ĕ ynP]9LӻnU{q돽K1`]c Q?~ _hx+jK*[Q[BP +dx]u3!ZyPct޳U`j3v{3n>bskh!]h <)ǯ:_̰Pt :VnA(>߬DJz lz <_JI}k߉WpoPoܿ1t?쌸;&#4&Wqr/`gLo~. KhG漏Tt[-PNט||E`"tT `+-oE[ݽK3Ftf1)? s7K/^!FL3{ߤtpVUdUrAʅ!Z9w i?>!c.}ɣ+mpc=ws3NNq:;"`)D5)_ӟ+Q&E2<=yZ̿WA-0j aAqk?w_bU5gl;a࣡Vzk!VKGh|z߉RIaGBġ-k2~xh !Hd]tǿy^2z|1WO޲`&@Zxc.c3L8wol 7 +jYcduuʎVV-䬟q,Lb*8.JA_˗"\HDi"?Xp4Zʰ*ҵٓ/KvO GI,Cp5-\YSFbi!:,dNޣ`ζӈꄶV=Jk1ypL覲~*NAϙ$| {7'*@!Mb,{!ՍH$ȫcjw0~}ҿw|Ef #̸՝ulfS+7 wyTb<@,_K r~X4Nf*5]!jO뙮O),a3WQ`g@*Uݓc-=H0Ptᓴ]EWľ" p| =nP=zI gYxb1nKS:g'(_VwbA=cUxeg0duKכܵ6THcZ!-F>|zх*Uu8 4g^>{6Uڋ3iУ/$wi 350!,6r[Fŗ 5INcTpb~&3_q)ت߷+|x.QEW~}=_ڔKFҪ+^Wu^} ׶o WDηɿ(nF^On[~ߤUO ^x̪Y%v9<.UWYBO厱ۉL8|rMӜg64qT;2ˣ-"\7ЃKE[0~ OcYcWQ42 f~= _G}Ģyh` ]i HRQ"uOW^EvE0?9,bhT%b8Xu͔"1)!x.cjB˕ GN8/ʾ1r[, &McK?ǃ:K;,Bߔujf`-NsPK:ALMyf pЄJ-EfNפZM(]89] ~8:G.|ko|.`·/!_L~e^@ QOl%ʵC@i+ X8>rm}~so??dyV8Qko k8ֺbL Z?3A95ZwXXͰ]u{PC-Ӑ}![ w[LDKa{M#ja|@$p:Ih&i֯@`0\=NP<X[D}t|1*Ib?u ۜf2N?zQsE^o9tZKsЄ uz?zBYf:s=9MtJf>n,<޼cMxQc8vw26&ϷH0> }B-a3~~R90fTާHjHRcb1(t8.Ua^ٯnl0:}5 ?څ!t(QMC KLq)u`BL9oXKB޸"햊*Ԡ23`P+ (׉/|6a:W(%cbK`[j Gju@ulD4J'^lܶX~`(5%\HȨT^ֵ3o%@$(-3Ezn]FL'~hU;A;DBaf4MCW(vY̤nJ^?iŷb+]#مcd`j̴u  %3LtwWM L\|X9Xp!B}e6 :ٚ|?t.jQNK;>^,wwM`[ki:\5#hnmYxnP =zyee75ԺPPL}} I YJEUu[Kk\K > 5^ߩHhg{=y0Mƒm[su7rLZ/\e95 zfSq%]^~ŷ=_8돿6 S(P* EW3UgDu-8Y3׼B wM-r2;V(~vq}ɰ_LKݷ UȊrKӶpiʗ2]_K͂&U af^]/}"Aa( 1Vdl8DkW}u{N5%| 2v58PQ+keh8BaZkҳ,zśAXsx wH^+nS)ڹVVSFR#)mulV\k4jTOT'UpMF?/te'y*fm1)We' Ak+NcwuP>Y/rk0HkF ^!K~||!:&,P--2d(e-f(3__Ffo31z˟H4OW2ŽxbYwUxukuYWQ6qo~/6[^}!K}:oR]r 6Qyo5o8~b1E`U#j+YFEuk).^8L%tSa:UKXCe2m jɋ4YU5仺_xZEZjNb3b \t@H JM-[}1wU!2aRk2l b̨8nPEYWi㞳t:^u;fvtnmb) ; 4o8T{2 Zi6s/^Kt Ε+Ѥ}V Gͳ*O+Aх* T<>Zk̥|I^zO%y49[:-Znb*a3:9} M}/W0 qavdXbCM"ome:ЉMk6pRMHB-B R\*±CRNIqj5fҫmx=hLe ³/N" w! cm,L3K"Ֆi7 66H֏*@ӧE`ڃbrRK0-b^)H0ZKP "wE;`bIfY`MAb+nE|_Tv?c2D ',s7c0\Yne*2͠NPxَYw̤kNUu?}f *i!YN.G V"oS(@J)k$b0!,NOlw+,x H f<*iIǻƦ0ݙW8l0i_i > JX0:?yGZn}娻5׿H\w%9{GUç')^=՘ϯސ:ߦ#(ᶫ~32|fM:Buf[_-+fp}Ϥ 5B7,՟ Ĵl4n4hZ|珼ʧ8%iV8wmk%HsB@>>Z~:.-pJ癓wրia^u vEVɈ;o\[Q*ƜA̵JU Je􋎱ITtqzU0Nah-8hF(hB p%iPEiu_%4ںRCX3ؚBhh"V4gr5O$44!`q@B@X 6!cKklA,  0?WV0̸(h( @0F«_3@P(H*Sp\칇)A QtsAK \wQv6 ڦmOX)s`)cq }\晆uO~3On_è勔BCGL)[j q ѫlq,W5:qĊ<?f*, ZSQ͖@P⾳{T_ mqտZ"MhWxU ~ FDޱYATb.-f_zÐQqaU\~|]շkyj#+GGސ]+j' +ڻk4ӎsr`էbb*=7 )ƾљ.ǿf S#ӟYC"VIK=xΠCsî|fbzgQc}?s7bA@֥٥{_~B@0`zN֌AҊt%J}P߻2-ZJuK:::Cz̻1-:⎿CW]\|*ja_Y@ˊƳ(Cl4oQ-*XYQj] U,6[~7_fշRc\A,Utp 3u Zu.ܰG-47RտCJXiEoF}8B@ u\}rVQ7p `*4tĽlkH[Mo{D}GT h+}X*rӢuz(jT˴;K hc;߼:zo[L׼TAE6IxRr {dfs0R(g)@ h*ҙig:@,rKrQ1 ňݭASԇXJ Pl!eó#HH-{ȥ crZ(4R@R` uX#iJt|n#sKS(L(L0fy?H-<~bٙ Z@XXXKRpGr@s.wj6[/.aDCxvCiz`8 S\ @[$ieiTSU]ҫՎuE(l]X-qeZ~g5gWo`2%n^\)Yϧ_!rDs"&#:)*Z.ۧ.Fɷ.K*<ŵ)~,+i݇9f9mimqlF [n%Sª)3p":CKV@9qfIq/=uę#gggt-7Vh6/:ATpDKB&F3.슆hQv\\n)mQR|GVOVo$4.˝R H`V2zBL+ԓ QL S'FE_ܰ= av\MɚEa=4ѻVr<Yy`++[!\gS 1|ƖMEAetqq&Rx^У-5(SsA(]F{-sJL SږД=#FsE]K9V$UM8r u7Q_ wWUXVfЬ̪3+|n@ &MF®[bp; ڶxQ VXj3cѸ 1T^1([7hZX%,*QǦ~y,A( 7n/vIC-x:S2ҵa t½Ŝ5๨.Px\B3z|IGT V]SkL0! 9U, p|r-.lAscZV]d$B>jX--t[X5*; !q7KiYc՘KPq%.]t!G WH>YEGYZ/Rmx&W]q*V 7lH۶PֹN.=Ҥu9?H|iz|Rn+Y!4}oMC֯=a]~}*a7hdNuKV׬JOu5C/[ K 6{΂V==z\8 f+\?jXJZ%u q\Sbʵ )V|Jk{C@/"}3I(-wĤWkFd({J]4cEBT:n:#W:ƜRV6") RfN+QB6j-nƹB,'6#4Z~XnD`/ r $Sb)zeod )hOCU̢'mj+ D5K @CpChs=҂L@n`'͹`d#8/E Sd0S9+2D ܮN63@z.W~gќL[w1LLjW{$5畆X2d*pM;fe{m_?r:s:Hj;::7x dNm* mF` m%AU^M23q7B;mЄe"@ʕ{}aZ3LKGpA4 <o/Qa`,)@Afl{&>rppX+Ƴa!/zQs@}/|GBsa16^M־<ʀ Z-1FԺ 1 b\1t@`bޜ;oĴ Fs"5.oF:06].77C>vJcK^qbڬ5;É=AtW|cbJskiuzFQa]eȺsx U0W&සXֳ:/F`yX^)I6qc+pY}.na1V B[!P8ο`\) >?efU&i̭)&د1.ZF#x`9DJ+% MeuA97ы\K7`W-Y0) &HYc A|&A6zSyi ~elH1%!"7t0T W<̔yG,xɛa5KN 뤱0bn*;仲{0/76Y/R=J5Bؘ(kqlӑ0X&ó~fH)^HZZx S#JmX-(W`7WOJay*0{4<+5b*դ'P k&4*C5N[P1h(]{AAV,^"4 [~ndz궡`4K:F4U L9f {X F/,|E`na4|s36 0# ӟE1Mt1cZ6Sʾ0J*{2γzںF-b d_jF&,LGK 5Xf}b ]뤺]Jfs"AWY|dMe)$kRؑ7Ԧ5_hF="-pȓ%ʇW^!0@uL3A}a7l;q 4M,4E̳֘^mav 'CʌG5Q.un*]4P~p;Hd=FdWE06Ӎ>CQ(DP=ݓ/͕i8rt PJl\q&l)Mz4ѕH)~%U("8  !N-P NjUQ)la-pY^Z%("R3]fޅլ3cO7sa0@9ʟ`),ObUnYeIso9K `^m$`λKRu ĵFPL7eZ2ͣxn5L+x4, [ͰO-w}atfYW船Uҥu@`#WQk;E"{_i(ln( c_Xf [oDƄn.lM]Gzp[J:w %XXL‘U;[H 6(8 XMLy):ˤ9X9ML b@]#p]`ykU$R 9őLy7cҁTཽQr-/GIi&88,cR EXH}Xz3?iPc}.P4;)h69wV"Jbzlb 㨱׼grGtQԝ* }&@/SR%o?K&0#c!Y#u.SeJ#x!~b!c3fw*5ߘt[-oA+\XQ{Mմ> |zksZǖ =b@W]s-u⣼Գ~A`|žAw_d4ܗrz$UW MWYRq1}%KK1}uK3,,X jh >rc&51i4PVo=&fB&4M%Va&HTw{./*.">2 G0>:)w>ApLRKĿ0`48]m4c#pɐj1~2R\* 1ᗨGRnq m!jmhǶZ^ شg"}* mcSQu +puba`/HCk4}B+Z!Hn=xܸvm@s3 w !!d>!(oiP܆~v>#3,ZeJC5rݕ{3w1:ͦN5pTye/Fe=e?F^CI'/x% z't,j32lCo?U b| WZ&r5+c[~-/>.d<ij~!vi>rIX+`xؚc27kVNaS }Js,c/3LտO𘱎菥xaמB )" 3`%-S%Ea^7"0O.EhaBB_h`SVUswMQc( 4|{bu-Il;x. rbcn j-ʛ`ؙOt_x'vǔ8{\-4S26v_`~(ٻR=&AJstXCpkG2~ܽhInS/1hZTֳN2wWH wWĩlQ3EʋhŽ⚯rET+aYA$0D0N?Y`ߩ>f(ؽ?2];hYG(DCucFCdB)2u6slF S/ZG쀬-aE3YrTJn2.h|7PjKW~`pbN85,"ͬe~Ү!.FlCwN!v]nYC[ uj`2{/TnhE6͘-vk@v]P'w=`A 5B֚˥g1h&<k-4 bہsd=(FfP11^͌V|,E<Dj^9o Q ~&e(wZG(YM/7iit"yY5GOhtl=f؏X99^wwԎÑ؀_94_m[a%t fQjKP*a:5"xxNRՐ[ C x3YUңv?Sf~# v[rshI"n lϩF^`P8ȋg#.M0 ]_ODȭ)tsgdžkFM{C!b3K=60>D,K_s*A $}Yb"BD)2JbB #3oeY'~98( 7@B88- y%jfՊo;ƷAAHoK[b.NP. \(-!@paH[` ^EcE`A,>M0ea g@G%L "P8QbG/ '@'DX 귢;P4):mt'RZnݻzԭWGlE%`z&GέFSc&= R_1-AtBFV#d>u n贙DDw}Tq ,Vm9 Xv_6}gM%#oif7c&29o1isq4w. s21w銶)6yB*%+Rb恘Joޒ?6L5eUF[:8eM0[Ó="pE-GW0f];A=b;6yg~ςЈz}]hD.P7ohQ=&E}=%Nhb)KזI>!L)Uu,H[k=t x p}`c05{i˯?ƂuK9̦+&7S-1J)rΰ~]Ӽ*PUu_-}#STGDNP[_j>_DP%\2~0}MR@OhMKrSq[®Vƫ9Y6)Su%I Nwl9RZXB8/xKS&ف! \?nҗ̂g[S\yCT>A!5,'YFTQ};ۤ}!×)ofψ%FPsq:׸( 5%RTEa@+Rj@6e@>_XV){hpLKqcRDS% %D9Ƭh-raw²&8 kĬ+I`ޜèW%us"rN0-62yVC_&gg ,/j}>Է(-R.(U.l> E԰YURkJ1Ĥ >,/0/^ Y1 B&=§I7Tk`<j?R+Ĩ='8 ܴ| & ] At/M>C(#6%%3Iܿs+ ~Ϝ!<ՁEy.|9#;^^E$<]z V'2B~`žbƨȚe( -]̠\"y1@,1 AW+UXNC췈ruigC+p :|Av%=$N/?S*yQ-wiQ#0*.X%؁xE1å%ڕ$h?iHhsq7ܘP =`X)Ruk=q)=D#Zz?K.[`4=OZY_w9q-*14~r1Z8J"{EZ-9!a ,GpS'xun8Js"t WD'42\^݇/Q!zWxL/ӗ1W?(CVةjG{΀tz2+jxe+2#OMv[Xs7Q'dlwO`=9}X_ēuW\V] Rk, Ձȇw9l{B" _]OX/4|=6d'WؙrS8=+xQdJ%#2Á20?b+yv&޲=LQU7%#l]=SOi4쿨3ܾEGa?.dq0G#{R~/ՉՇU>*70+ߨQV& #fwK.G4-yғc@棴w`xg4,* J"TQV o1(TKo4wc4f7z?<-N^OX"fOݼyj-]NBB Fmяf,ID`Tq+*}f-񻃴`agO Ӵb"(k߄覙>"x> |LLbWLFa@dH|)񈦰=W[}=2UJOSw-cIx;/h/eJ E7`r3k;ҷ|m;Bnс&>8=AF5¦E⡼@mSnmCۭU]m{BGy?:- nO ^m% JdY+;4tsu {6zZ_Z"ˀmz&^L6{yhሓD"^bv<ͱjN)_ d?9q̌>'bQoDQ~67L6387^)/v?xVUEw.Vya}І냵ԳM}f,?WxY{K,湩j_O V/9b ujvo"R2F\WՁ5]I1rqVӏb;ЍK6k9>_=%,. )zĜ<ƏGYmJ"n"EY~KhZG5`DkB="YlvxUcc)G.TBeoM!L Yh;EE3| pny*#!P벱q!k̤^4H $́6C}(h6c"?$o\~!iӞ01lQ:/ Q 7=CIe9PBt[]ԃ# U?L}_]!y3ӓ8?R5ķ'/EzUXz#gĘ`]/ p%US:LA-ݩqXߙdP j&f7K?WjM [b6{Ŝ0S1 XPO0z#Q&xo0QUxWGog$AwYzѢIE|~^"췩t} > &ߔx8UPeةK,^/pHGT]$p~Nz1=cGygjAc> (UiI.ƩܲxB*+0i.jw[goBu @ 𲄇njT^6qDJ2G,`c./oܲNUBUNWYS*=.Am>/5lϼ kbo%Z_#ޥ6S R럈t3w?pMLf30'@wDcd?[s8F!%c5n~Oz~+S13V'\:c ~ҲF I& #X!+RX>v=He]SgwEgBV'F=)N*t0LƗ +G^b&ϖ-a\"D_MyqWzCaD^0]K/@<@!2#_P&g&&ȟ:Ļ п…phaeY|(0h-#ZJ;cqD0!et;X|N.hpيQ^5"ۜ^_e}~&VD[Dҷ{ YX& \:U+4jrK~)q% 13X yٴ߸^~Z8)Q"FqkڅA70(L֟a+nD?ddVF:hk5dvC0Gǻ*Y_nZ-+x7k4^.F$? ,Q}_ĸ4tWh wX7)>lqzlقͮE-jA(NrOXO ߸z]cp23/A4y鏈 C>\>!&8UOA[Q6c9ձ:1<]ҽDRa_t!Ȱ3Ͼ1 GгoGAYk'៨犯a17Kt׼Z˻Q( *Pc%#\>S a,GxaZA֬F#wIJQXE[n-0W}?[䪕wP/mA&AElGgfwKV~n[RL$DvtzfY~W`mʫj a z 2sz)uUتC.:⊜b %6Rk&%]_Mtr,8`Uc ͗;[CGcD2kG0< ?ٔWH`TUʬ}T~A ]Ʌ80s>Gc2Nn%#tQ(RpTAN1d`f[E c~ap<s,i,IɴT Xt$>,=V؁R<2^!ko8(XUvIJ^DCJ'3/ ٬6[qtRȞplg0]8rZ$i]gEhsA3j?(V=.EKy1c88Oa LU#5fUKQԴP}{͆ae#C1~=eoTS?(-W/H~pЉ.'ѽzB =^&%ZM` #r!WgO}ec鍪%DnU1r/F(rTe*̯b.)tK& RlXQxmsn6 'ujl,vk׏UB:* y3 &xY"! F%CQZH Hf6u|JD85/ &fs`yܪȠ+1fR/43ym~&o9( rQ\Zl-kr}X;5#(kPh¿D+b&FVyHů{Tt}I_Eh * h:+8}mC1ͱEXGV$B,lag`. 5wGQ cIoAd:a+uQDH#BDCB'.>f3ݥV,[-V;h&YXnpR; Ρw˦ T!e0f d,էx%rbbaN&u^檒 KU;E2iȀQE_W= 9@^j@E`RyUٙЁlYOJVCJlPzOLmr`/(vX(R]]hcSfO%;:*ٱ`w|#dn^Njd?iUL`mNW912Ia3sc@oCn8$=`tNHSٕ #ԂG |`c:+"=`\J׏K\^!@:&"_f'-t5lHmk2ʙ~IJ^~_` q^f9)stf3ً- V2h/>cyMͿ? >SGϚs~'O5<'v?zMI|g<?| rG؟>\<'1~IGOtTrf6z͞m>G4N3V&Nfwޤ|?m S\6ϊofasteval-0.2.4/examples/simple-vars.rs010066400017500001750000000010441361026471300161750ustar0000000000000000// usage: cargo run --release --example simple-vars use std::collections::BTreeMap; fn main() -> Result<(), fasteval::Error> { let mut map : BTreeMap = BTreeMap::new(); map.insert("x".to_string(), 1.0); map.insert("y".to_string(), 2.0); map.insert("z".to_string(), 3.0); let val = fasteval::ez_eval(r#"x + print("y:",y) + z"#, &mut map)?; // | // prints "y: 2" to stderr and then evaluates to 2.0 assert_eq!(val, 6.0); Ok(()) } fasteval-0.2.4/examples/slab.rs010066400017500001750000000023571361026467200146700ustar0000000000000000// usage: cargo run --release --example slab use std::collections::BTreeMap; use fasteval::Evaler; // use this trait so we can call eval(). fn main() -> Result<(), fasteval::Error> { let parser = fasteval::Parser::new(); let mut slab = fasteval::Slab::new(); // See the `parse` documentation to understand why we use `from` like this: let expr_ref = parser.parse("x + 1", &mut slab.ps)?.from(&slab.ps); // Let's evaluate the expression a couple times with different 'x' values: let mut map : BTreeMap = BTreeMap::new(); map.insert("x".to_string(), 1.0); let val = expr_ref.eval(&slab, &mut map)?; assert_eq!(val, 2.0); map.insert("x".to_string(), 2.5); let val = expr_ref.eval(&slab, &mut map)?; assert_eq!(val, 3.5); // Now, let's re-use the Slab for a new expression. // (This is much cheaper than allocating a new Slab.) // The Slab gets cleared by 'parse()', so you must avoid using // the old expr_ref after parsing the new expression. // One simple way to avoid this problem is to shadow the old variable: let expr_ref = parser.parse("x * 10", &mut slab.ps)?.from(&slab.ps); let val = expr_ref.eval(&slab, &mut map)?; assert_eq!(val, 25.0); Ok(()) } fasteval-0.2.4/examples/unsafe-vars.rs010066400017500001750000000034651361026746600162060ustar0000000000000000// usage: cargo run --release --features unsafe-vars --example unsafe-vars fn main() -> Result<(), fasteval::Error> { #[cfg(not(feature="unsafe-vars"))] { panic!("You must enable the `unsafe-vars` feature to run this example: cargo run --release --features unsafe-vars --example unsafe-vars"); } // Allow compilation even when the `unsafe-vars` feature is not enabled. // This is important so that `cargo test` can succeed. #[cfg(feature="unsafe-vars")] { use fasteval::Evaler; // use this trait so we can call eval(). use fasteval::Compiler; // use this trait so we can call compile(). let parser = fasteval::Parser::new(); let mut slab = fasteval::Slab::new(); // The Unsafe Variable will use a pointer to read this memory location: // You must make sure that this variable stays in-scope as long as the // expression is in-use. let mut deg : f64 = 0.0; // Unsafe Variables must be registered before 'parse()'. // (Normal Variables only need definitions during the 'eval' phase.) unsafe { slab.ps.add_unsafe_var("deg".to_string(), °); } // `add_unsafe_var()` only exists if the `unsafe-vars` feature is enabled: `cargo test --features unsafe-vars` let expr_str = "sin(deg/360 * 2*pi())"; let compiled = parser.parse(expr_str, &mut slab.ps)?.from(&slab.ps).compile(&slab.ps, &mut slab.cs); let mut ns = fasteval::EmptyNamespace; // We only define unsafe variables, not normal variables, // so EmptyNamespace is fine. for d in 0..360 { deg = d as f64; let val = fasteval::eval_compiled!(compiled, &slab, &mut ns); eprintln!("sin({}°) = {}", deg, val); } Ok(()) } } fasteval-0.2.4/src/compiler.rs010066400017500001750000001065121361026177600145320ustar0000000000000000//! This module compiles parsed `Expression`s into an optimized AST node called an `Instruction`. //! The compiled form is much faster, especially for constants. //! //! # Compile-time Optimizations //! //! ## Constant Folding //! Operations with constants can be calculated at compile time so //! they don't need to be calculated during `eval()`. //! //! For example, `1 + x + 1` will be compiled into `x + 2`, saving some time during `eval()`. //! //! If the entire expression only consists of constants (no variables), //! then the expression can be reduced to a final result at compile time, //! resulting in near-native speed during `eval()`. //! //! ## Algebraic Simplification //! * Subtraction is converted to Addition. //! * Division is converted to Multiplication. //! * Built-in functions with constant arguments are evaluated. //! * Constant terms are combined. //! * Logical operator short-circuits are applied and no-op branches are discarded. //! //! ## Optimized Memory Layout and Execution //! * Variable-length `Expression`/`Value` AST nodes are converted into constant-sized `Instruction` nodes. //! * The `IC` enumeration helps to eliminate expensive function calls. use crate::slab::{ParseSlab, CompileSlab}; use crate::parser::{Expression, ExprPair, Value, UnaryOp::{self, EPos, ENeg, ENot, EParentheses}, BinaryOp::{self, EOR, EAND, ENE, EEQ, EGTE, ELTE, EGT, ELT, EAdd, ESub, EMul, EDiv, EMod, EExp}, StdFunc::{self, EVar, EFunc, EFuncInt, EFuncCeil, EFuncFloor, EFuncAbs, EFuncSign, EFuncLog, EFuncRound, EFuncMin, EFuncMax, EFuncE, EFuncPi, EFuncSin, EFuncCos, EFuncTan, EFuncASin, EFuncACos, EFuncATan, EFuncSinH, EFuncCosH, EFuncTanH, EFuncASinH, EFuncACosH, EFuncATanH}, PrintFunc}; #[cfg(feature="unsafe-vars")] use crate::parser::StdFunc::EUnsafeVar; /// `true` --> `1.0`, `false` --> `0.0` #[macro_export] macro_rules! bool_to_f64 { ($b:expr) => { if $b { 1.0 } else { 0.0 } }; } /// An `InstructionI` represents an index into `Slab.cs.instrs`. /// /// It behaves much like a pointer or reference, but it is 'safe' (unlike a raw /// pointer) and is not managed by the Rust borrow checker (unlike a reference). #[derive(Debug, PartialEq, Copy, Clone)] pub struct InstructionI(pub usize); /// This enumeration boosts performance because it eliminates expensive function calls for constant values. #[derive(Debug, PartialEq)] pub enum IC { I(InstructionI), C(f64), } macro_rules! instr_to_ic { ($cslab:ident, $instr:ident) => { match $instr { IConst(c) => IC::C(c), _ => IC::I($cslab.push_instr($instr)), } } } macro_rules! ic_to_instr { ($cslab:expr, $dst:ident, $ic:ident) => { match $ic { IC::C(c) => { $dst = IConst(*c); &$dst } IC::I(i) => get_instr!($cslab,i), } } } /// An `Instruction` is an optimized AST node resulting from compilation. #[derive(Debug, PartialEq)] pub enum Instruction { //---- Primitive Value Types: IConst(f64), //---- Unary Ops: // Parentheses is a noop // Pos is a noop INeg(InstructionI), INot(InstructionI), IInv(InstructionI), //---- Binary Math Ops: IAdd(InstructionI, IC), // A Sub(x) is converted to an Add(Neg(x)). IMul(InstructionI, IC), // A Div(n,d) is converted to a Mul(n,Inv(d)). IMod{dividend:IC, divisor:IC}, IExp{base:IC, power:IC}, //---- Binary Comparison Ops: ILT(IC, IC), ILTE(IC, IC), IEQ(IC, IC), INE(IC, IC), IGTE(IC, IC), IGT(IC, IC), //---- Binary Logic Ops: IOR(InstructionI, IC), IAND(InstructionI, IC), //---- Callables: IVar(String), #[cfg(feature="unsafe-vars")] IUnsafeVar{name:String, ptr:*const f64}, IFunc{name:String, args:Vec}, IFuncInt(InstructionI), IFuncCeil(InstructionI), IFuncFloor(InstructionI), IFuncAbs(InstructionI), IFuncSign(InstructionI), IFuncLog{base:IC, of:IC}, IFuncRound{modulus:IC, of:IC}, IFuncMin(InstructionI, IC), IFuncMax(InstructionI, IC), IFuncSin(InstructionI), IFuncCos(InstructionI), IFuncTan(InstructionI), IFuncASin(InstructionI), IFuncACos(InstructionI), IFuncATan(InstructionI), IFuncSinH(InstructionI), IFuncCosH(InstructionI), IFuncTanH(InstructionI), IFuncASinH(InstructionI), IFuncACosH(InstructionI), IFuncATanH(InstructionI), IPrintFunc(PrintFunc), // Not optimized (it would be pointless because of i/o bottleneck). } use Instruction::{IConst, INeg, INot, IInv, IAdd, IMul, IMod, IExp, ILT, ILTE, IEQ, INE, IGTE, IGT, IOR, IAND, IVar, IFunc, IFuncInt, IFuncCeil, IFuncFloor, IFuncAbs, IFuncSign, IFuncLog, IFuncRound, IFuncMin, IFuncMax, IFuncSin, IFuncCos, IFuncTan, IFuncASin, IFuncACos, IFuncATan, IFuncSinH, IFuncCosH, IFuncTanH, IFuncASinH, IFuncACosH, IFuncATanH, IPrintFunc}; #[cfg(feature="unsafe-vars")] use Instruction::IUnsafeVar; impl Default for Instruction { fn default() -> Self { IConst(std::f64::NAN) } } /// You must `use` the `Compiler` trait before you can call `.compile()` on parsed `Expression`s. pub trait Compiler { /// Turns a parsed `Expression` into a compiled `Instruction`. /// /// Cannot fail, unless you run out of memory. fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction; } #[derive(Debug)] struct ExprSlice<'s> { first: &'s Value, pairs: Vec<&'s ExprPair>, } impl<'s> ExprSlice<'s> { fn new(first:&Value) -> ExprSlice<'_> { ExprSlice{ first, pairs:Vec::with_capacity(8), } } fn from_expr(expr:&Expression) -> ExprSlice<'_> { let mut sl = ExprSlice::new(&expr.first); for exprpairref in expr.pairs.iter() { sl.pairs.push(exprpairref) } sl } fn split(&self, bop:BinaryOp, dst:&mut Vec>) { dst.push(ExprSlice::new(&self.first)); for exprpair in self.pairs.iter() { if exprpair.0==bop { dst.push(ExprSlice::new(&exprpair.1)); } else { match dst.last_mut() { Some(cur) => cur.pairs.push(exprpair), None => (), // unreachable } } } } fn split_multi(&self, search:&[BinaryOp], xsdst:&mut Vec>, opdst:&mut Vec<&'s BinaryOp>) { xsdst.push(ExprSlice::new(&self.first)); for exprpair in self.pairs.iter() { if search.contains(&exprpair.0) { xsdst.push(ExprSlice::new(&exprpair.1)); opdst.push(&exprpair.0); } else { match xsdst.last_mut() { Some(cur) => cur.pairs.push(exprpair), None => (), // unreachable } } } } } /// Uses [`EPSILON`](https://doc.rust-lang.org/core/f64/constant.EPSILON.html) to determine equality of two `f64`s. #[macro_export] macro_rules! f64_eq { ($l:ident, $r:literal) => { ($l-$r).abs() <= 8.0*std::f64::EPSILON }; ($l:ident, $r:ident) => { ($l-$r).abs() <= 8.0*std::f64::EPSILON }; ($l:expr, $r:literal) => { ($l-$r).abs() <= 8.0*std::f64::EPSILON }; ($l:expr, $r:expr) => { (($l)-($r)).abs() <= 8.0*std::f64::EPSILON }; } /// Uses [`EPSILON`](https://doc.rust-lang.org/core/f64/constant.EPSILON.html) to determine inequality of two `f64`s. /// /// This is exactly the same as saying `!f64_eq(x,y)` but it is slightly more efficient. #[macro_export] macro_rules! f64_ne { ($l:ident, $r:literal) => { ($l-$r).abs() > 8.0*std::f64::EPSILON }; ($l:ident, $r:ident) => { ($l-$r).abs() > 8.0*std::f64::EPSILON }; ($l:expr, $r:literal) => { ($l-$r).abs() > 8.0*std::f64::EPSILON }; ($l:expr, $r:expr) => { (($l)-($r)).abs() > 8.0*std::f64::EPSILON }; } fn neg_wrap(instr:Instruction, cslab:&mut CompileSlab) -> Instruction { if let IConst(c) = instr { IConst(-c) } else if let INeg(i) = instr { cslab.take_instr(i) } else { INeg(cslab.push_instr(instr)) } } fn not_wrap(instr:Instruction, cslab:&mut CompileSlab) -> Instruction { if let IConst(c) = instr { IConst(bool_to_f64!(f64_eq!(c,0.0))) } else if let INot(i) = instr { cslab.take_instr(i) } else { INot(cslab.push_instr(instr)) } } fn inv_wrap(instr:Instruction, cslab:&mut CompileSlab) -> Instruction { if let IConst(c) = instr { IConst(1.0/c) } else if let IInv(i) = instr { cslab.take_instr(i) } else { IInv(cslab.push_instr(instr)) } } fn compile_mul(instrs:Vec, cslab:&mut CompileSlab) -> Instruction { let mut out = IConst(1.0); let mut out_set = false; let mut const_prod = 1.0; for instr in instrs { if let IConst(c) = instr { const_prod *= c; // Floats don't overflow. } else { if out_set { out = IMul(cslab.push_instr(out), IC::I(cslab.push_instr(instr))); } else { out = instr; out_set = true; } } } if f64_ne!(const_prod,1.0) { if out_set { out = IMul(cslab.push_instr(out), IC::C(const_prod)); } else { out = IConst(const_prod); } } out } fn compile_add(instrs:Vec, cslab:&mut CompileSlab) -> Instruction { let mut out = IConst(0.0); let mut out_set = false; let mut const_sum = 0.0; for instr in instrs { if let IConst(c) = instr { const_sum += c; // Floats don't overflow. } else { if out_set { out = IAdd(cslab.push_instr(out), IC::I(cslab.push_instr(instr))); } else { out = instr; out_set = true; } } } if f64_ne!(const_sum,0.0) { if out_set { out = IAdd(cslab.push_instr(out), IC::C(const_sum)); } else { out = IConst(const_sum); } } out } pub(crate) fn log(base:f64, n:f64) -> f64 { // Can't use floating point in 'match' patterns. :( if f64_eq!(base,2.0) { return n.log2(); } if f64_eq!(base,10.0) { return n.log10(); } n.log(base) } // Can't inline recursive functions: fn push_mul_leaves(instrs:&mut Vec, cslab:&mut CompileSlab, li:InstructionI, ric:IC) { // Take 'r' before 'l' for a chance for more efficient memory usage: match ric { IC::I(ri) => { let instr = cslab.take_instr(ri); if let IMul(rli,rric) = instr { push_mul_leaves(instrs,cslab,rli,rric); } else { instrs.push(instr); } } IC::C(c) => instrs.push(IConst(c)), }; let instr = cslab.take_instr(li); if let IMul(lli,lric) = instr { push_mul_leaves(instrs,cslab,lli,lric); } else { instrs.push(instr); } } fn push_add_leaves(instrs:&mut Vec, cslab:&mut CompileSlab, li:InstructionI, ric:IC) { // Take 'r' before 'l' for a chance for more efficient memory usage: match ric { IC::I(ri) => { let instr = cslab.take_instr(ri); if let IAdd(rli,rric) = instr { push_add_leaves(instrs,cslab,rli,rric); } else { instrs.push(instr); } } IC::C(c) => instrs.push(IConst(c)), }; let instr = cslab.take_instr(li); if let IAdd(lli,lric) = instr { push_add_leaves(instrs,cslab,lli,lric); } else { instrs.push(instr); } } impl Compiler for ExprSlice<'_> { fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction { // Associative: (2+3)+4 = 2+(3+4) // Commutative: 1+2 = 2+1 // // Only Only // Neither Associative Commutative Both // ------- ----------- ----------- ---- // GTE (none) (none) OR // LTE AND // GT NE // LT EQ // Minus (opt with neg & add) Plus // Div (opt with inv & mul) Mul // Mod // Exp // Find the lowest-priority BinaryOp: let mut lowest_op = match self.pairs.first() { Some(p0) => p0.0, None => return self.first.compile(pslab,cslab), }; for exprpair in self.pairs.iter() { if exprpair.0::with_capacity(4); let mut xss = Vec::::with_capacity(ops.len()+1); self.split_multi(&[EEQ, ENE, ELT, EGT, ELTE, EGTE], &mut xss, &mut ops); let mut out = match xss.first() { Some(xs) => xs.compile(pslab,cslab), None => IConst(std::f64::NAN), // unreachable }; for (i,op) in ops.into_iter().enumerate() { let instr = match xss.get(i+1) { Some(xs) => xs.compile(pslab,cslab), None => IConst(std::f64::NAN), // unreachable }; if let IConst(l) = out { if let IConst(r) = instr { out = match op { EEQ => IConst(bool_to_f64!(f64_eq!(l,r))), ENE => IConst(bool_to_f64!(f64_ne!(l,r))), ELT => IConst(bool_to_f64!(l IConst(bool_to_f64!(l>r)), ELTE => IConst(bool_to_f64!(l<=r)), EGTE => IConst(bool_to_f64!(l>=r)), _ => IConst(std::f64::NAN), // unreachable }; continue; } } out = match op { EEQ => IEQ(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)), ENE => INE(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)), ELT => ILT(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)), EGT => IGT(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)), ELTE => ILTE(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)), EGTE => IGTE(instr_to_ic!(cslab,out), instr_to_ic!(cslab,instr)), _ => IConst(std::f64::NAN), // unreachable }; } return out; } match lowest_op { EOR => { let mut xss = Vec::::with_capacity(4); self.split(EOR, &mut xss); let mut out = IConst(0.0); let mut out_set = false; for xs in xss.iter() { let instr = xs.compile(pslab,cslab); if out_set { out = IOR(cslab.push_instr(out), instr_to_ic!(cslab,instr)); } else { if let IConst(c) = instr { if f64_ne!(c,0.0) { return instr; } // out = instr; // Skip this 0 value (mostly so I don't complicate my logic in 'if out_set' since I can assume that any set value is non-const). // out_set = true; } else { out = instr; out_set = true; } } } out } EAND => { let mut xss = Vec::::with_capacity(4); self.split(EAND, &mut xss); let mut out = IConst(1.0); let mut out_set = false; for xs in xss.iter() { let instr = xs.compile(pslab,cslab); if let IConst(c) = instr { if f64_eq!(c,0.0) { return instr; } } if out_set { if let IConst(_) = out { // If we get here, we know that the const is non-zero. out = instr; } else { out = IAND(cslab.push_instr(out), instr_to_ic!(cslab,instr)); } } else { out = instr; out_set = true; } } out } EAdd => { let mut xss = Vec::::with_capacity(4); self.split(EAdd, &mut xss); let mut instrs = Vec::::with_capacity(xss.len()); for xs in xss { let instr = xs.compile(pslab,cslab); if let IAdd(li,ric) = instr { push_add_leaves(&mut instrs,cslab,li,ric); // Flatten nested structures like "x - 1 + 2 - 3". } else { instrs.push(instr); } } compile_add(instrs,cslab) } ESub => { // Note: We don't need to push_add_leaves from here because Sub has a higher precedence than Add. let mut xss = Vec::::with_capacity(4); self.split(ESub, &mut xss); let mut instrs = Vec::::with_capacity(xss.len()); for (i,xs) in xss.into_iter().enumerate() { let instr = xs.compile(pslab,cslab); if i==0 { instrs.push(instr); } else { instrs.push(neg_wrap(instr,cslab)); } } compile_add(instrs,cslab) } EMul => { let mut xss = Vec::::with_capacity(4); self.split(EMul, &mut xss); let mut instrs = Vec::::with_capacity(xss.len()); for xs in xss { let instr = xs.compile(pslab,cslab); if let IMul(li,ric) = instr { push_mul_leaves(&mut instrs,cslab,li,ric); // Flatten nested structures like "deg/360 * 2*pi()". } else { instrs.push(instr); } } compile_mul(instrs,cslab) } EDiv => { // Note: We don't need to push_mul_leaves from here because Div has a higher precedence than Mul. let mut xss = Vec::::with_capacity(4); self.split(EDiv, &mut xss); let mut instrs = Vec::::with_capacity(xss.len()); for (i,xs) in xss.into_iter().enumerate() { let instr = xs.compile(pslab,cslab); if i==0 { instrs.push(instr); } else { instrs.push(inv_wrap(instr,cslab)); } } compile_mul(instrs,cslab) } // EDiv => { // let mut xss = Vec::::with_capacity(4); // self.split(EDiv, &mut xss); // let mut out = IConst(1.0); let mut out_set = false; // let mut const_prod = 1.0; // let mut is_first = true; // for xs in xss.iter() { // let instr = xs.compile(pslab,cslab); // if let IConst(c) = instr { // if is_first { // const_prod *= c; // Floats don't overflow. // } else { // const_prod /= c; // } // } else { // if is_first { // if out_set { // out = IMul(cslab.push_instr(out), cslab.push_instr(instr)); // } else { // out = instr; // out_set = true; // } // } else { // let instr = inv_wrap(instr,cslab); // if out_set { // out = IMul(cslab.push_instr(out), cslab.push_instr(instr)); // } else { // out = instr; // out_set = true; // } // } // } // is_first = false; // } // if f64_ne!(const_prod,1.0) { // if out_set { // out = IMul(cslab.push_instr(out), cslab.push_instr(IConst(const_prod))); // } else { // out = IConst(const_prod); // } // } // out // } EMod => { let mut xss = Vec::::with_capacity(2); self.split(EMod, &mut xss); let mut out = IConst(0.0); let mut out_set = false; for xs in xss.iter() { let instr = xs.compile(pslab,cslab); if out_set { if let IConst(dividend) = out { if let IConst(divisor) = instr { out = IConst(dividend%divisor); continue; } } out = IMod{dividend:instr_to_ic!(cslab,out), divisor:instr_to_ic!(cslab,instr)}; } else { out = instr; out_set = true; } } out } EExp => { // Right-to-Left Associativity let mut xss = Vec::::with_capacity(2); self.split(EExp, &mut xss); let mut out = IConst(0.0); let mut out_set = false; for xs in xss.into_iter().rev() { let instr = xs.compile(pslab,cslab); if out_set { if let IConst(power) = out { if let IConst(base) = instr { out = IConst(base.powf(power)); continue; } } out = IExp{base:instr_to_ic!(cslab,instr), power:instr_to_ic!(cslab,out)}; } else { out = instr; out_set = true; } } out } // EExp => { // Left-to-Right Associativity // let mut xss = Vec::::with_capacity(2); // self.split(EExp, &mut xss); // let mut pow_instrs = Vec::::with_capacity(xss.len()-1); // let mut base = IConst(0.0); // for (i,xs) in xss.into_iter().enumerate() { // let instr = xs.compile(pslab,cslab); // if i==0 { // base = instr; // } else { // pow_instrs.push(instr); // } // } // let power = compile_mul(pow_instrs,cslab); // if let IConst(b) = base { // if let IConst(p) = power { // return IConst(b.powf(p)); // } // } // IExp{base:cslab.push_instr(base), power:cslab.push_instr(power)} // } ENE | EEQ | EGTE | ELTE | EGT | ELT => IConst(std::f64::NAN), // unreachable } } } impl Compiler for Expression { fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction { let top = ExprSlice::from_expr(&self); top.compile(pslab,cslab) } } impl Compiler for Value { fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction { match self { Value::EConstant(c) => IConst(*c), Value::EUnaryOp(u) => u.compile(pslab,cslab), Value::EStdFunc(f) => f.compile(pslab,cslab), Value::EPrintFunc(pf) => IPrintFunc(pf.clone()), } } } impl Compiler for UnaryOp { fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction { match self { EPos(i) => get_val!(pslab,i).compile(pslab,cslab), ENeg(i) => { let instr = get_val!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(-c) } else { neg_wrap(instr,cslab) } } ENot(i) => { let instr = get_val!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(bool_to_f64!(f64_eq!(c,0.0))) } else { not_wrap(instr,cslab) } } EParentheses(i) => get_expr!(pslab,i).compile(pslab,cslab), } } } impl Compiler for StdFunc { fn compile(&self, pslab:&ParseSlab, cslab:&mut CompileSlab) -> Instruction { match self { EVar(name) => IVar(name.clone()), #[cfg(feature="unsafe-vars")] EUnsafeVar{name,ptr} => IUnsafeVar{name:name.clone(), ptr:*ptr}, EFunc{name, args:xis} => { let mut args = Vec::::with_capacity(xis.len()); for xi in xis { let instr = get_expr!(pslab,xi).compile(pslab,cslab); args.push(instr_to_ic!(cslab,instr)); } IFunc{name:name.clone(), args} } EFuncInt(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.trunc()) } else { IFuncInt(cslab.push_instr(instr)) } } EFuncCeil(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.ceil()) } else { IFuncCeil(cslab.push_instr(instr)) } } EFuncFloor(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.floor()) } else { IFuncFloor(cslab.push_instr(instr)) } } EFuncAbs(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.abs()) } else { IFuncAbs(cslab.push_instr(instr)) } } EFuncSign(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.signum()) } else { IFuncSign(cslab.push_instr(instr)) } } EFuncLog{base:baseopt, expr:i} => { let base = match baseopt { Some(bi) => get_expr!(pslab,bi).compile(pslab,cslab), None => IConst(10.0), }; let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(b) = base { if let IConst(n) = instr { return IConst(log(b,n)); } } IFuncLog{base:instr_to_ic!(cslab,base), of:instr_to_ic!(cslab,instr)} } EFuncRound{modulus:modopt, expr:i} => { let modulus = match modopt { Some(mi) => get_expr!(pslab,mi).compile(pslab,cslab), None => IConst(1.0), }; let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(m) = modulus { if let IConst(n) = instr { return IConst( (n/m).round() * m ); // Floats don't overflow. } } IFuncRound{modulus:instr_to_ic!(cslab,modulus), of:instr_to_ic!(cslab,instr)} } EFuncMin{first:fi, rest:is} => { let first = get_expr!(pslab,fi).compile(pslab,cslab); let mut rest = Vec::::with_capacity(is.len()); for i in is { rest.push(get_expr!(pslab,i).compile(pslab,cslab)); } let mut out = IConst(0.0); let mut out_set = false; let mut const_min = 0.0; let mut const_min_set = false; if let IConst(f) = first { const_min = f; const_min_set = true; } else { out = first; out_set = true; } for instr in rest { if let IConst(f) = instr { if const_min_set { if f { let first = get_expr!(pslab,fi).compile(pslab,cslab); let mut rest = Vec::::with_capacity(is.len()); for i in is { rest.push(get_expr!(pslab,i).compile(pslab,cslab)); } let mut out = IConst(0.0); let mut out_set = false; let mut const_max = 0.0; let mut const_max_set = false; if let IConst(f) = first { const_max = f; const_max_set = true; } else { out = first; out_set = true; } for instr in rest { if let IConst(f) = instr { if const_max_set { if f>const_max { const_max=f; } } else { const_max = f; const_max_set = true; } } else { if out_set { out = IFuncMax(cslab.push_instr(out), IC::I(cslab.push_instr(instr))); } else { out = instr; out_set = true; } } } if const_max_set { if out_set { out = IFuncMax(cslab.push_instr(out), IC::C(const_max)); } else { out = IConst(const_max); // out_set = true; // Comment out so the compiler doesn't complain about unused assignments. } } //assert!(out_set); out } EFuncE => IConst(std::f64::consts::E), EFuncPi => IConst(std::f64::consts::PI), EFuncSin(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.sin()) } else { IFuncSin(cslab.push_instr(instr)) } } EFuncCos(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.cos()) } else { IFuncCos(cslab.push_instr(instr)) } } EFuncTan(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.tan()) } else { IFuncTan(cslab.push_instr(instr)) } } EFuncASin(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.asin()) } else { IFuncASin(cslab.push_instr(instr)) } } EFuncACos(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.acos()) } else { IFuncACos(cslab.push_instr(instr)) } } EFuncATan(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.atan()) } else { IFuncATan(cslab.push_instr(instr)) } } EFuncSinH(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.sinh()) } else { IFuncSinH(cslab.push_instr(instr)) } } EFuncCosH(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.cosh()) } else { IFuncCosH(cslab.push_instr(instr)) } } EFuncTanH(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.tanh()) } else { IFuncTanH(cslab.push_instr(instr)) } } EFuncASinH(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.asinh()) } else { IFuncASinH(cslab.push_instr(instr)) } } EFuncACosH(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.acosh()) } else { IFuncACosH(cslab.push_instr(instr)) } } EFuncATanH(i) => { let instr = get_expr!(pslab,i).compile(pslab,cslab); if let IConst(c) = instr { IConst(c.atanh()) } else { IFuncATanH(cslab.push_instr(instr)) } } } } } fasteval-0.2.4/src/error.rs010066400017500001750000000060031360642263200140360ustar0000000000000000//! This module contains `fasteval`'s Error type: an `enum` that contains all errors //! that can be produced by the `fasteval` API. use std::fmt; /// This is the error type used in `fasteval`'s `Result`s. /// /// For performance reasons, `fasteval` makes an effort to always return `Error`s /// instead of using `panic!()`. #[derive(Debug, Clone, PartialEq)] pub enum Error { /// Too many Expressions/Values/Instructions were stored in the Slab. /// /// A Slab is pre-allocated at the beginning of the process, and it is /// not re-sized. You can use `Slab::with_capacity()` to increase the /// number of items that can be stored. SlabOverflow, /// Returned by `EvalNamespace::create_cached()`. /// /// An entry with the same name already exists in the Namespace. If you /// intend to overwrite existing entries, use EvalNamespace::set_cached()` /// instead. AlreadyExists, /// Reached an unexpected End Of Input during parsing. EOF, /// Reached an unexpected End Of Input during parsing. /// /// The `String` field contains information about what was being parsed /// when the EOF was reached. EofWhileParsing(String), /// UTF8 decoding error. /// /// The `String` field contains information about what was being parsed /// when the UTF8 error occurred. Utf8ErrorWhileParsing(String), /// The expression string input was too long. /// /// This is a safety check that prevents malicious inputs that would /// be expensive to parse. TooLong, /// The expression was too recursive. /// /// This is a safety check that prevents malicious inputs that would /// be expensive to parse. TooDeep, /// An expression was parsed, but there is still input data remaining. /// /// The `String` field contains the un-parsed input data. UnparsedTokensRemaining(String), /// A value was expected, but invalid input data was found. InvalidValue, /// An error occurred during the parsing of a f64 number. /// /// The `String` field contains the data that caused the error. ParseF64(String), /// The expected input data was not found. /// /// The `String` field tells you what was expected. Expected(String), /// A function was called with the wrong arguments. /// /// The `String` field contains information about the expected arguments. WrongArgs(String), /// The expression tried to use an undefined variable/function. /// /// You can define variables/functions with a Namespace. Undefined(String), /// This error should never occur because it is only produced by code paths /// that should never execute. This is more performant than using the /// `unreachable!()` macro. Unreachable, } impl std::error::Error for Error { // The defaults are fine for now. } impl fmt::Display for Error { fn fmt(&self, f:&mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{:?}", self) // Re-use Debug for now... } } fasteval-0.2.4/src/evaler.rs010066400017500001750000000727571360731025600142050ustar0000000000000000//! This module evaluates parsed `Expression`s and compiled `Instruction`s. //! //! Everything can be evaluated using the `.eval()` method, but compiled //! `Instruction`s also have the option of using the `eval_compiled!()` macro //! which is much faster for common cases. use crate as fasteval; use crate::error::Error; use crate::slab::Slab; use crate::evalns::EvalNamespace; use crate::parser::{Expression, Value::{self, EConstant, EUnaryOp, EStdFunc, EPrintFunc}, UnaryOp::{self, EPos, ENeg, ENot, EParentheses}, BinaryOp::{self, EAdd, ESub, EMul, EDiv, EMod, EExp, ELT, ELTE, EEQ, ENE, EGTE, EGT, EOR, EAND}, StdFunc::{self, EVar, EFunc, EFuncInt, EFuncCeil, EFuncFloor, EFuncAbs, EFuncSign, EFuncLog, EFuncRound, EFuncMin, EFuncMax, EFuncE, EFuncPi, EFuncSin, EFuncCos, EFuncTan, EFuncASin, EFuncACos, EFuncATan, EFuncSinH, EFuncCosH, EFuncTanH, EFuncASinH, EFuncACosH, EFuncATanH}, PrintFunc, ExpressionOrString::{EExpr, EStr}, remove_no_panic}; #[cfg(feature="unsafe-vars")] use crate::parser::StdFunc::EUnsafeVar; use crate::compiler::{log, IC, Instruction::{self, IConst, INeg, INot, IInv, IAdd, IMul, IMod, IExp, ILT, ILTE, IEQ, INE, IGTE, IGT, IOR, IAND, IVar, IFunc, IFuncInt, IFuncCeil, IFuncFloor, IFuncAbs, IFuncSign, IFuncLog, IFuncRound, IFuncMin, IFuncMax, IFuncSin, IFuncCos, IFuncTan, IFuncASin, IFuncACos, IFuncATan, IFuncSinH, IFuncCosH, IFuncTanH, IFuncASinH, IFuncACosH, IFuncATanH, IPrintFunc}}; #[cfg(feature="unsafe-vars")] use crate::compiler::Instruction::IUnsafeVar; use std::collections::BTreeSet; use std::f64::consts; use std::fmt; /// The same as `evaler.eval(&slab, &mut ns)`, but more efficient for common cases. /// /// This macro is exactly the same as [`eval_compiled_ref!()`](macro.eval_compiled_ref.html) /// but is more efficient if you have ownership of the evaler. /// /// Only use this for compiled expressions. (If you use it for interpreted /// expressions, it will work but will always be slower than calling `eval()` directly.) /// /// This macro is able to eliminate function calls for constants and Unsafe Variables. /// Since evaluation is a performance-critical operation, saving some function /// calls actually makes a huge performance difference. /// #[macro_export] macro_rules! eval_compiled { ($evaler:ident, $slab_ref:expr, $ns_mut:expr) => { if let fasteval::IConst(c) = $evaler { c } else { #[cfg(feature="unsafe-vars")] { if let fasteval::IUnsafeVar{ptr, ..} = $evaler { unsafe { *ptr } } else { $evaler.eval($slab_ref, $ns_mut)? } } #[cfg(not(feature="unsafe-vars"))] $evaler.eval($slab_ref, $ns_mut)? } }; ($evaler:expr, $slab_ref:expr, $ns_mut:expr) => { { let evaler = $evaler; eval_compiled!(evaler, $slab_ref, $ns_mut) } }; } /// The same as `evaler_ref.eval(&slab, &mut ns)`, but more efficient for common cases. /// /// This macro is exactly the same as [`eval_compiled!()`](macro.eval_compiled.html) but /// is useful when you hold a reference to the evaler, rather than having ownership of it. /// /// Only use this for compiled expressions. (If you use it for interpreted /// expressions, it will work but will always be slower than calling `eval()` directly.) /// /// This macro is able to eliminate function calls for constants and Unsafe Variables. /// Since evaluation is a performance-critical operation, saving some function /// calls actually makes a huge performance difference. /// #[macro_export] macro_rules! eval_compiled_ref { ($evaler:ident, $slab_ref:expr, $ns_mut:expr) => { if let fasteval::IConst(c) = $evaler { *c } else { #[cfg(feature="unsafe-vars")] { if let fasteval::IUnsafeVar{ptr, ..} = $evaler { unsafe { **ptr } } else { $evaler.eval($slab_ref, $ns_mut)? } } #[cfg(not(feature="unsafe-vars"))] $evaler.eval($slab_ref, $ns_mut)? } }; ($evaler:expr, $slab_ref:expr, $ns_mut:expr) => { { let evaler = $evaler; eval_compiled_ref!(evaler, $slab_ref, $ns_mut) } }; } macro_rules! eval_ic_ref { ($ic:ident, $slab_ref:ident, $ns_mut:expr) => { match $ic { IC::C(c) => *c, IC::I(i) => { let instr_ref = get_instr!($slab_ref.cs,i); #[cfg(feature="unsafe-vars")] { if let fasteval::IUnsafeVar{ptr, ..} = instr_ref { unsafe { **ptr } } else { instr_ref.eval($slab_ref, $ns_mut)? } } #[cfg(not(feature="unsafe-vars"))] instr_ref.eval($slab_ref, $ns_mut)? } } } } /// You must `use` this trait so you can call `.eval()`. pub trait Evaler : fmt::Debug { /// Evaluate this `Expression`/`Instruction` and return an `f64`. /// /// Returns a `fasteval::Error` if there are any problems, such as undefined variables. fn eval(&self, slab:&Slab, ns:&mut impl EvalNamespace) -> Result; /// Don't call this directly. Use `var_names()` instead. /// /// This exists because of ternary short-circuits; they prevent us from /// getting a complete list of vars just by doing eval() with a clever /// callback. fn _var_names(&self, slab:&Slab, dst:&mut BTreeSet); /// Returns a list of variables and custom functions that are used by this `Expression`/`Instruction`. fn var_names(&self, slab:&Slab) -> BTreeSet { let mut set = BTreeSet::new(); self._var_names(slab,&mut set); set } } impl Evaler for Expression { fn _var_names(&self, slab:&Slab, dst:&mut BTreeSet) { self.first._var_names(slab,dst); for pair in &self.pairs { pair.1._var_names(slab,dst); } } fn eval(&self, slab:&Slab, ns:&mut impl EvalNamespace) -> Result { // Order of operations: 1) ^ 2) */ 3) +- // Exponentiation should be processed right-to-left. Think of what 2^3^4 should mean: // 2^(3^4)=2417851639229258349412352 <--- I choose this one. https://codeplea.com/exponentiation-associativity-options // (2^3)^4=4096 // Direction of processing doesn't matter for Addition and Multiplication: // (((3+4)+5)+6)==(3+(4+(5+6))), (((3*4)*5)*6)==(3*(4*(5*6))) // ...But Subtraction and Division must be processed left-to-right: // (((6-5)-4)-3)!=(6-(5-(4-3))), (((6/5)/4)/3)!=(6/(5/(4/3))) // // ---- Go code, for comparison ---- // // vals,ops:=make([]float64, len(e)/2+1),make([]BinaryOp, len(e)/2) // // for i:=0; i = Vec::with_capacity(self.0.len()/2+1); // let mut ops : Vec = Vec::with_capacity(self.0.len()/2 ); // for (i,tok) in self.0.iter().enumerate() { // match tok { // EValue(val) => { // if i%2==1 { return Err(KErr::new("Found value at odd index")) } // match ns.eval_bubble(val) { // Ok(f) => vals.push(f), // Err(e) => return Err(e.pre(&format!("eval_bubble({:?})",val))), // } // } // EBinaryOp(bop) => { // if i%2==0 { return Err(KErr::new("Found binaryop at even index")) } // ops.push(*bop); // } // } // } // Code for new Expression data structure: let mut vals = Vec::::with_capacity(self.pairs.len()+1); let mut ops = Vec::::with_capacity(self.pairs.len()); vals.push(self.first.eval(slab,ns)?); for pair in self.pairs.iter() { ops.push(pair.0); vals.push(pair.1.eval(slab,ns)?); } // ---- Go code, for comparison ---- // evalOp:=func(i int) { // result:=ops[i]._Eval(vals[i], vals[i+1]) // vals=append(append(vals[:i], result), vals[i+2:]...) // ops=append(ops[:i], ops[i+1:]...) // } // rtol:=func(s BinaryOp) { for i:=len(ops)-1; i>=0; i-- { if ops[i]==s { evalOp(i) } } } // ltor:=func(s BinaryOp) { // loop: // for i:=0; i, ops:&mut Vec, search:BinaryOp) { for i in (0..ops.len()).rev() { let op = match ops.get(i) { Some(op) => *op, None => EOR, // unreachable }; if op==search { let res = op.binaryop_eval(vals.get(i), vals.get(i+1)); match vals.get_mut(i) { Some(val_ref) => *val_ref=res, None => (), // unreachable }; remove_no_panic(vals, i+1); remove_no_panic(ops, i); } } } #[inline(always)] fn ltor(vals:&mut Vec, ops:&mut Vec, search:BinaryOp) { let mut i = 0; loop { match ops.get(i) { None => break, Some(op) => { if *op==search { let res = op.binaryop_eval(vals.get(i), vals.get(i+1)); match vals.get_mut(i) { Some(val_ref) => *val_ref=res, None => (), // unreachable }; remove_no_panic(vals, i+1); remove_no_panic(ops, i); } else { i=i+1; } } } } } #[inline(always)] fn ltor_multi(vals:&mut Vec, ops:&mut Vec, search:&[BinaryOp]) { let mut i = 0; loop { match ops.get(i) { None => break, Some(op) => { if search.contains(op) { let res = op.binaryop_eval(vals.get(i), vals.get(i+1)); match vals.get_mut(i) { Some(val_ref) => *val_ref=res, None => (), // unreachable }; remove_no_panic(vals, i+1); remove_no_panic(ops, i); } else { i=i+1; } } } } } // Keep the order of these statements in-sync with parser.rs BinaryOp priority values: rtol(&mut vals, &mut ops, EExp); // https://codeplea.com/exponentiation-associativity-options ltor(&mut vals, &mut ops, EMod); ltor(&mut vals, &mut ops, EDiv); rtol(&mut vals, &mut ops, EMul); ltor(&mut vals, &mut ops, ESub); rtol(&mut vals, &mut ops, EAdd); ltor_multi(&mut vals, &mut ops, &[ELT, EGT, ELTE, EGTE, EEQ, ENE]); // TODO: Implement Python-style a Ok(*val), None => Err(Error::Unreachable), } } } impl Evaler for Value { fn _var_names(&self, slab:&Slab, dst:&mut BTreeSet) { match self { EConstant(_) => (), EUnaryOp(u) => u._var_names(slab,dst), EStdFunc(f) => f._var_names(slab,dst), EPrintFunc(f) => f._var_names(slab,dst), }; } fn eval(&self, slab:&Slab, ns:&mut impl EvalNamespace) -> Result { match self { EConstant(c) => Ok(*c), EUnaryOp(u) => u.eval(slab,ns), EStdFunc(f) => f.eval(slab,ns), EPrintFunc(f) => f.eval(slab,ns), } } } impl Evaler for UnaryOp { fn _var_names(&self, slab:&Slab, dst:&mut BTreeSet) { match self { EPos(val_i) | ENeg(val_i) | ENot(val_i) => get_val!(slab.ps,val_i)._var_names(slab,dst), EParentheses(expr_i) => get_expr!(slab.ps,expr_i)._var_names(slab,dst), } } fn eval(&self, slab:&Slab, ns:&mut impl EvalNamespace) -> Result { match self { EPos(val_i) => get_val!(slab.ps,val_i).eval(slab,ns), ENeg(val_i) => Ok(-get_val!(slab.ps,val_i).eval(slab,ns)?), ENot(val_i) => Ok(bool_to_f64!(f64_eq!(get_val!(slab.ps,val_i).eval(slab,ns)?,0.0))), EParentheses(expr_i) => get_expr!(slab.ps,expr_i).eval(slab,ns), } } } impl BinaryOp { // Non-standard eval interface (not generalized yet): fn binaryop_eval(self, left_opt:Option<&f64>, right_opt:Option<&f64>) -> f64 { // Passing 'self' by value is more efficient than pass-by-reference. let left = match left_opt { Some(l) => *l, None => return std::f64::NAN, }; let right = match right_opt { Some(r) => *r, None => return std::f64::NAN, }; match self { EAdd => left+right, // Floats don't overflow. ESub => left-right, EMul => left*right, EDiv => left/right, EMod => left%right, //left - (left/right).trunc()*right EExp => left.powf(right), ELT => bool_to_f64!(left bool_to_f64!(left<=right), EEQ => bool_to_f64!(f64_eq!(left,right)), ENE => bool_to_f64!(f64_ne!(left,right)), EGTE => bool_to_f64!(left>=right), EGT => bool_to_f64!(left>right), EOR => if f64_ne!(left,0.0) { left } else { right }, EAND => if f64_eq!(left,0.0) { left } else { right }, } } } macro_rules! eval_var { ($ns:ident, $name:ident, $args:expr, $keybuf:expr) => { match $ns.lookup($name,$args,$keybuf) { Some(f) => Ok(f), None => Err(Error::Undefined($name.to_string())), } }; } impl Evaler for StdFunc { fn _var_names(&self, slab:&Slab, dst:&mut BTreeSet) { match self { #[cfg(feature="unsafe-vars")] EUnsafeVar{name, ..} => { dst.insert(name.clone()); } EVar(s) => { dst.insert(s.clone()); } EFunc{name, ..} => { dst.insert(name.clone()); } EFuncInt(xi) | EFuncCeil(xi) | EFuncFloor(xi) | EFuncAbs(xi) | EFuncSign(xi) | EFuncSin(xi) | EFuncCos(xi) | EFuncTan(xi) | EFuncASin(xi) | EFuncACos(xi) | EFuncATan(xi) | EFuncSinH(xi) | EFuncCosH(xi) | EFuncTanH(xi) | EFuncASinH(xi) | EFuncACosH(xi) | EFuncATanH(xi) => get_expr!(slab.ps,xi)._var_names(slab,dst), EFuncE | EFuncPi => (), EFuncLog{base:opt,expr} | EFuncRound{modulus:opt,expr} => { match opt { Some(xi) => get_expr!(slab.ps,xi)._var_names(slab,dst), None => (), } get_expr!(slab.ps,expr)._var_names(slab,dst); } EFuncMin{first,rest} | EFuncMax{first,rest} => { get_expr!(slab.ps,first)._var_names(slab,dst); for xi in rest { get_expr!(slab.ps,xi)._var_names(slab,dst); } } }; } fn eval(&self, slab:&Slab, ns:&mut impl EvalNamespace) -> Result { match self { // These match arms are ordered in a way that I feel should deliver good performance. // (I don't think this ordering actually affects the generated code, though.) #[cfg(feature="unsafe-vars")] EUnsafeVar{ptr, ..} => unsafe { Ok(**ptr) }, EVar(name) => eval_var!(ns, name, Vec::new(), unsafe{ &mut *(&slab.ps.char_buf as *const _ as *mut _) }), EFunc{name, args:xis} => { let mut args = Vec::with_capacity(xis.len()); for xi in xis { args.push(get_expr!(slab.ps,xi).eval(slab,ns)?) } eval_var!(ns, name, args, unsafe{ &mut *(&slab.ps.char_buf as *const _ as *mut _) }) } EFuncLog{base:base_opt, expr:expr_i} => { let base = match base_opt { Some(b_expr_i) => get_expr!(slab.ps,b_expr_i).eval(slab,ns)?, None => 10.0, }; let n = get_expr!(slab.ps,expr_i).eval(slab,ns)?; Ok(log(base,n)) } EFuncSin(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.sin()), EFuncCos(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.cos()), EFuncTan(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.tan()), EFuncASin(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.asin()), EFuncACos(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.acos()), EFuncATan(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.atan()), EFuncSinH(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.sinh()), EFuncCosH(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.cosh()), EFuncTanH(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.tanh()), EFuncASinH(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.asinh()), EFuncACosH(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.acosh()), EFuncATanH(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.atanh()), EFuncRound{modulus:modulus_opt, expr:expr_i} => { let modulus = match modulus_opt { Some(m_expr_i) => get_expr!(slab.ps,m_expr_i).eval(slab,ns)?, None => 1.0, }; Ok((get_expr!(slab.ps,expr_i).eval(slab,ns)?/modulus).round() * modulus) } EFuncAbs(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.abs()), EFuncSign(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.signum()), EFuncInt(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.trunc()), EFuncCeil(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.ceil()), EFuncFloor(expr_i) => Ok(get_expr!(slab.ps,expr_i).eval(slab,ns)?.floor()), EFuncMin{first:first_i, rest} => { let mut min = get_expr!(slab.ps,first_i).eval(slab,ns)?; let mut saw_nan = min.is_nan(); for x_i in rest.iter() { min = min.min(get_expr!(slab.ps,x_i).eval(slab,ns)?); saw_nan = saw_nan || min.is_nan(); } if saw_nan { Ok(std::f64::NAN) } else { Ok(min) } } EFuncMax{first:first_i, rest} => { let mut max = get_expr!(slab.ps,first_i).eval(slab,ns)?; let mut saw_nan = max.is_nan(); for x_i in rest.iter() { max = max.max(get_expr!(slab.ps,x_i).eval(slab,ns)?); saw_nan = saw_nan || max.is_nan(); } if saw_nan { Ok(std::f64::NAN) } else { Ok(max) } } EFuncE => Ok(consts::E), EFuncPi => Ok(consts::PI), } } } impl Evaler for PrintFunc { fn _var_names(&self, slab:&Slab, dst:&mut BTreeSet) { for x_or_s in &self.0 { match x_or_s { EExpr(xi) => get_expr!(slab.ps,xi)._var_names(slab,dst), EStr(_) => (), }; } } fn eval(&self, slab:&Slab, ns:&mut impl EvalNamespace) -> Result { let mut val = 0f64; fn process_str(s:&str) -> String { s.replace("\\n","\n").replace("\\t","\t") } if let Some(EStr(fmtstr)) = self.0.first() { if fmtstr.contains('%') { // printf mode: //let fmtstr = process_str(fmtstr); return Err(Error::WrongArgs("printf formatting is not yet implemented".to_string())); // TODO: Make a pure-rust sprintf libarary. //return Ok(val); } } // Normal Mode: let mut out = String::with_capacity(16); for (i,a) in self.0.iter().enumerate() { if i>0 { out.push(' '); } match a { EExpr(e_i) => { val = get_expr!(slab.ps,e_i).eval(slab,ns)?; out.push_str(&val.to_string()); } EStr(s) => out.push_str(&process_str(s)) } } eprintln!("{}", out); Ok(val) } } impl Evaler for Instruction { fn _var_names(&self, slab:&Slab, dst:&mut BTreeSet) { match self { #[cfg(feature="unsafe-vars")] IUnsafeVar{name, ..} => { dst.insert(name.clone()); } IVar(s) => { dst.insert(s.clone()); } IFunc{name, ..} => { dst.insert(name.clone()); } IConst(_) => (), INeg(ii) | INot(ii) | IInv(ii) | IFuncInt(ii) | IFuncCeil(ii) | IFuncFloor(ii) | IFuncAbs(ii) | IFuncSign(ii) | IFuncSin(ii) | IFuncCos(ii) | IFuncTan(ii) | IFuncASin(ii) | IFuncACos(ii) | IFuncATan(ii) | IFuncSinH(ii) | IFuncCosH(ii) | IFuncTanH(ii) | IFuncASinH(ii) | IFuncACosH(ii) | IFuncATanH(ii) => get_instr!(slab.cs,ii)._var_names(slab,dst), ILT(lic,ric) | ILTE(lic,ric) | IEQ(lic,ric) | INE(lic,ric) | IGTE(lic,ric) | IGT(lic,ric) | IMod{dividend:lic, divisor:ric} | IExp{base:lic, power:ric} | IFuncLog{base:lic, of:ric} | IFuncRound{modulus:lic, of:ric} => { let mut iconst : Instruction; ic_to_instr!(slab.cs,iconst,lic)._var_names(slab,dst); ic_to_instr!(slab.cs,iconst,ric)._var_names(slab,dst); } IAdd(li,ric) | IMul(li,ric) | IOR(li,ric) | IAND(li,ric) | IFuncMin(li,ric) | IFuncMax(li,ric) => { get_instr!(slab.cs,li)._var_names(slab,dst); let iconst : Instruction; ic_to_instr!(slab.cs,iconst,ric)._var_names(slab,dst); } IPrintFunc(pf) => pf._var_names(slab,dst), } } fn eval(&self, slab:&Slab, ns:&mut impl EvalNamespace) -> Result { match self { // I have manually ordered these match arms in a way that I feel should deliver good performance. // (I don't think this ordering actually affects the generated code, though.) IMul(li,ric) => { Ok( eval_compiled_ref!(get_instr!(slab.cs,li), slab, ns) * eval_ic_ref!(ric,slab,ns) ) } IAdd(li,ric) => { Ok( eval_compiled_ref!(get_instr!(slab.cs,li), slab, ns) + eval_ic_ref!(ric, slab, ns) ) } IExp{base, power} => { Ok( eval_ic_ref!(base, slab, ns).powf( eval_ic_ref!(power, slab, ns) ) ) } INeg(i) => Ok(-eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns)), IInv(i) => Ok(1.0/eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns)), IVar(name) => eval_var!(ns, name, Vec::new(), unsafe{ &mut *(&slab.ps.char_buf as *const _ as *mut _) }), IFunc{name, args:ics} => { let mut args = Vec::with_capacity(ics.len()); for ic in ics { args.push( eval_ic_ref!(ic, slab, ns) ); } eval_var!(ns, name, args, unsafe{ &mut *(&slab.ps.char_buf as *const _ as *mut _) }) }, IFuncLog{base:baseic, of:ofic} => { let base = eval_ic_ref!(baseic, slab, ns); let of = eval_ic_ref!(ofic, slab, ns); Ok(log(base,of)) } IFuncSin(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).sin() ), IFuncCos(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).cos() ), IFuncTan(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).tan() ), IFuncASin(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).asin() ), IFuncACos(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).acos() ), IFuncATan(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).atan() ), IFuncSinH(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).sinh() ), IFuncCosH(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).cosh() ), IFuncTanH(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).tanh() ), IFuncASinH(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).asinh() ), IFuncACosH(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).acosh() ), IFuncATanH(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).atanh() ), IFuncRound{modulus:modic, of:ofic} => { let modulus = eval_ic_ref!(modic, slab, ns); let of = eval_ic_ref!(ofic, slab, ns); Ok( (of/modulus).round() * modulus ) } IMod{dividend, divisor} => { Ok( eval_ic_ref!(dividend, slab, ns) % eval_ic_ref!(divisor, slab, ns) ) } IFuncAbs(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).abs() ), IFuncSign(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).signum() ), IFuncInt(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).trunc() ), IFuncCeil(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).ceil() ), IFuncFloor(i) => Ok( eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns).floor() ), IFuncMin(li,ric) => { let left = eval_compiled_ref!(get_instr!(slab.cs,li), slab, ns); let right = eval_ic_ref!(ric, slab, ns); if left.is_nan() || right.is_nan() { return Ok(std::f64::NAN) } // I need to implement NAN checks myself because the f64.min() function says that if one number is NaN, the other will be returned. if left { let left = eval_compiled_ref!(get_instr!(slab.cs,li), slab, ns); let right = eval_ic_ref!(ric, slab, ns); if left.is_nan() || right.is_nan() { return Ok(std::f64::NAN) } if left>right { Ok(left) } else { Ok(right) } } IEQ(left, right) => { Ok( bool_to_f64!(f64_eq!(eval_ic_ref!(left, slab, ns), eval_ic_ref!(right, slab, ns))) ) } INE(left, right) => { Ok( bool_to_f64!(f64_ne!(eval_ic_ref!(left, slab, ns), eval_ic_ref!(right, slab, ns))) ) } ILT(left, right) => { Ok( bool_to_f64!(eval_ic_ref!(left, slab, ns) < eval_ic_ref!(right, slab, ns)) ) } ILTE(left, right) => { Ok( bool_to_f64!(eval_ic_ref!(left, slab, ns) <= eval_ic_ref!(right, slab, ns)) ) } IGTE(left, right) => { Ok( bool_to_f64!(eval_ic_ref!(left, slab, ns) >= eval_ic_ref!(right, slab, ns)) ) } IGT(left, right) => { Ok( bool_to_f64!(eval_ic_ref!(left, slab, ns) > eval_ic_ref!(right, slab, ns)) ) } INot(i) => Ok(bool_to_f64!(f64_eq!(eval_compiled_ref!(get_instr!(slab.cs,i), slab, ns),0.0))), IAND(lefti, rightic) => { let left = eval_compiled_ref!(get_instr!(slab.cs,lefti), slab, ns); if f64_eq!(left,0.0) { Ok(left) } else { Ok(eval_ic_ref!(rightic, slab, ns)) } } IOR(lefti, rightic) => { let left = eval_compiled_ref!(get_instr!(slab.cs,lefti), slab, ns); if f64_ne!(left,0.0) { Ok(left) } else { Ok(eval_ic_ref!(rightic, slab, ns)) } } IPrintFunc(pf) => pf.eval(slab,ns), // Put these last because you should be using the eval_compiled*!() macros to eliminate function calls. IConst(c) => Ok(*c), #[cfg(feature="unsafe-vars")] IUnsafeVar{ptr, ..} => unsafe { Ok(**ptr) }, } } } fasteval-0.2.4/src/evalns.rs010066400017500001750000000431331361311002100141620ustar0000000000000000//! Evaluation Namespaces used for Variable-lookups and custom Functions. //! //! Several Evaluation Namespace types are defined, each with their own advantages: //! * [`EmptyNamespace`](#emptynamespace) -- Useful when you know that your //! expressions don't need to look up any variables. //! * BTreeMap -- A simple way to define variables and functions with a map. //! Type aliases: [StringToF64Namespace](#stringtof64namespace), //! [StrToF64Namespace](#strtof64namespace), //! [StringToCallbackNamespace](#stringtocallbacknamespace), //! [StrToCallbackNamespace](#strtocallbacknamespace) //! * [`FnMut(&str,Vec) -> Option`](#callback-fnmutstrvec---option) -- //! Define variables and custom functions using a callback function. //! * [`CachedCallbackNamespace`](#cachedcallbacknamespace) -- Like the above //! callback-based Namespace, but results are cached so the callback is not //! queried more than once for a given variable. //! * Vec> -- Define variables with layered maps. //! Each layer is a separate 'scope'. Higher layers take precedence //! over lower layers. Very useful for creating scoped higher-level-languages. //! Type alias: [LayeredStringToF64Namespace](#layeredstringtof64namespace) //! //! # Examples //! //! ## EmptyNamespace //! ``` //! fn main() -> Result<(), fasteval::Error> { //! let mut ns = fasteval::EmptyNamespace; //! //! let val = fasteval::ez_eval("sin(pi()/2)", &mut ns)?; //! assert_eq!(val, 1.0); //! //! Ok(()) //! } //! ``` //! //! ## StringToF64Namespace //! ``` //! fn main() -> Result<(), fasteval::Error> { //! let mut ns = fasteval::StringToF64Namespace::new(); //! ns.insert("x".to_string(), 2.0); //! //! let val = fasteval::ez_eval("x * (x + 1)", &mut ns)?; //! assert_eq!(val, 6.0); //! //! Ok(()) //! } //! ``` //! //! ## StrToF64Namespace //! ``` //! fn main() -> Result<(), fasteval::Error> { //! let mut ns = fasteval::StrToF64Namespace::new(); //! ns.insert("x", 2.0); //! //! let val = fasteval::ez_eval("x * (x + 1)", &mut ns)?; //! assert_eq!(val, 6.0); //! //! Ok(()) //! } //! ``` //! //! ## Callback: FnMut(&str,Vec) -> Option //! ``` //! fn main() -> Result<(), fasteval::Error> { //! let mut num_lookups = 0; //! let mut cb = |name:&str, args:Vec| -> Option { //! num_lookups += 1; //! match name { //! "x" => Some(2.0), //! _ => None, //! } //! }; //! //! let val = fasteval::ez_eval("x * (x + 1)", &mut cb)?; //! assert_eq!(val, 6.0); //! assert_eq!(num_lookups, 2); // Notice that 'x' was looked-up twice. //! //! Ok(()) //! } //! ``` //! //! ## StringToCallbackNamespace //! ``` //! fn main() -> Result<(), fasteval::Error> { //! let mut ns = fasteval::StringToCallbackNamespace::new(); //! ns.insert("x".to_string(), Box::new(|_args| 2.0)); //! ns.insert("double".to_string(), Box::new(|args| { //! args.get(0).map(|arg0| arg0*2.0).unwrap_or(std::f64::NAN) //! })); //! //! let val = fasteval::ez_eval("double(x + 1) + 1", &mut ns)?; //! assert_eq!(val, 7.0); //! //! Ok(()) //! } //! ``` //! //! ## StrToCallbackNamespace //! ``` //! fn main() -> Result<(), fasteval::Error> { //! let mut ns = fasteval::StrToCallbackNamespace::new(); //! ns.insert("x", Box::new(|_args| 2.0)); //! ns.insert("double", Box::new(|args| { //! args.get(0).map(|arg0| arg0*2.0).unwrap_or(std::f64::NAN) //! })); //! //! let val = fasteval::ez_eval("double(x + 1) + 1", &mut ns)?; //! assert_eq!(val, 7.0); //! //! Ok(()) //! } //! ``` //! //! ## CachedCallbackNamespace //! ``` //! fn main() -> Result<(), fasteval::Error> { //! let mut num_lookups = 0; //! let val = { //! let cb = |name:&str, args:Vec| -> Option { //! num_lookups += 1; //! match name { //! "x" => { //! // Pretend that it is very expensive to calculate this, //! // and that's why we want to use the CachedCallbackNamespace cache. //! for i in 0..1000000 { /* do work */ } // Fake Work for this example. //! Some(2.0) //! } //! _ => None, //! } //! }; //! let mut ns = fasteval::CachedCallbackNamespace::new(cb); //! //! fasteval::ez_eval("x * (x + 1)", &mut ns)? //! }; //! assert_eq!(val, 6.0); //! assert_eq!(num_lookups, 1); // Notice that only 1 lookup occurred. //! // The second 'x' value was cached. //! //! Ok(()) //! } //! ``` //! //! ## LayeredStringToF64Namespace //! ``` //! fn main() -> Result<(), fasteval::Error> { //! let mut layer1 = fasteval::StringToF64Namespace::new(); //! layer1.insert("x".to_string(), 2.0); //! layer1.insert("y".to_string(), 3.0); //! //! let mut layers : fasteval::LayeredStringToF64Namespace = vec![layer1]; //! //! let val = fasteval::ez_eval("x * y", &mut layers)?; //! assert_eq!(val, 6.0); //! //! // Let's add another layer which shadows the previous one: //! let mut layer2 = fasteval::StringToF64Namespace::new(); //! layer2.insert("x".to_string(), 3.0); //! layers.push(layer2); //! //! let val = fasteval::ez_eval("x * y", &mut layers)?; //! assert_eq!(val, 9.0); //! //! // Remove the top layer and we'll be back to what we had before: //! layers.pop(); //! //! let val = fasteval::ez_eval("x * y", &mut layers)?; //! assert_eq!(val, 6.0); //! //! Ok(()) //! } //! ``` //! //! ## Custom Namespace Types //! //! If the pre-defined Namespace types aren't perfect for your application, you //! can create your own namespace type -- just implemenet the `EvalNamespace` //! trait (and maybe the `Cached` and `Layered` traits too). Also, as //! `fasteval` becomes more mature and is used for more real-life things, I //! will continue to add more useful Namespace types. //! //! Here are a few ideas of possibly-useful custom Namespace types: //! //! * Vec)->Option> -- This would be a `Layered` //! namespace, with each layer having its own callback. Really powerful! //! //! * CachedCallbacksNamespace -- Same as above, but with a cache for each //! layer. Good for expensive look-ups. use crate::error::Error; use std::collections::BTreeMap; //---- Types: /// All `fasteval` Namespaces must implement the `EvalNamespace` trait. pub trait EvalNamespace { /// Perform a variable/function lookup. /// /// May return cached values. fn lookup(&mut self, name:&str, args:Vec, keybuf:&mut String) -> Option; } /// Cache operations for `EvalNamespace`s. /// /// Implement this trait if your Namespace type uses a cache. pub trait Cached { /// Creates a new cached entry. If an entry with the same name already /// exists, an [`AlreadyExists` Error](../error/enum.Error.html#variant.AlreadyExists) is returned. fn cache_create(&mut self, name:String, val:f64) -> Result<(),Error>; /// Sets a cached entry. It doesn't matter whether or not a previous value /// existed with this name. fn cache_set( &mut self, name:String, val:f64); /// Clear all cached entries. Values will be recalculated and cached /// again the next time they are looked up. fn cache_clear(&mut self); } //// I don't want to put this into the public API until it is needed. // pub trait Layered { // fn push(&mut self); // fn pop(&mut self); // } /// Use `EmptyNamespace` when you know that you won't be looking up any variables. /// /// It is a zero-sized type, which means it gets optimized-away at compile time. /// /// [See module-level documentation for example.](index.html#emptynamespace) /// pub struct EmptyNamespace; /// `CachedCallbackNamespace` is useful when your variable/function lookups are expensive. /// /// Each variable+args combo will only be looked up once, and then it will be /// cached and re-used for subsequent lookups. /// /// [See module-level documentation for example.](index.html#cachedcallbacknamespace) /// pub struct CachedCallbackNamespace<'a> { cache:BTreeMap, cb :Box)->Option + 'a>, // I think a reference would be more efficient than a Box, but then I would need to use a funky 'let cb=|n|{}; Namespace::new(&cb)' syntax. The Box results in a super convenient pass-the-cb-by-value API interface. } //// I am commenting these out until I need them in real-life. //// (I don't want to add things to the public API until necessary.) // pub struct CachedLayeredNamespace<'a> { // caches:Vec>, // cb :Box)->Option + 'a>, // } // pub struct Bubble<'a,NS> where NS:EvalNamespace+Layered+'a { // ns :&'a mut NS, // count:usize, // } //---- Impls: #[inline(always)] fn key_from_nameargs<'a,'b:'a>(keybuf:&'a mut String, name:&'b str, args:&[f64]) -> &'a str { if args.is_empty() { name } else { keybuf.clear(); keybuf.reserve(name.len() + args.len()*20); keybuf.push_str(name); for f in args { keybuf.push_str(" , "); keybuf.push_str(&f.to_string()); }; keybuf.as_str() } } /// Type alias for `BTreeMap` pub type StringToF64Namespace = BTreeMap; impl EvalNamespace for StringToF64Namespace { #[inline] fn lookup(&mut self, name:&str, args:Vec, keybuf:&mut String) -> Option { let key = key_from_nameargs(keybuf, name, &args); self.get(key).copied() } } /// Type alias for `BTreeMap<&'static str,f64>` pub type StrToF64Namespace = BTreeMap<&'static str,f64>; impl EvalNamespace for StrToF64Namespace { #[inline] fn lookup(&mut self, name:&str, args:Vec, keybuf:&mut String) -> Option { let key = key_from_nameargs(keybuf, name, &args); self.get(key).copied() } } /// Type alias for `BTreeMap)->f64>>` /// /// This namespace type provides a very convenient way to register variables /// and custom functions. It is a bit slower than a pure callback, but it has /// isolation and composition advantages. pub type StringToCallbackNamespace<'a> = BTreeMap)->f64 + 'a>>; impl EvalNamespace for StringToCallbackNamespace<'_> { #[inline] fn lookup(&mut self, name:&str, args:Vec, _keybuf:&mut String) -> Option { if let Some(f) = self.get_mut(name) { Some(f(args)) } else { None } } } /// Type alias for `BTreeMap<&'static str, Box)->f64>>` /// /// This namespace type provides a very convenient way to register variables /// and custom functions. It is a bit slower than a pure callback, but it has /// isolation and composition advantages. pub type StrToCallbackNamespace<'a> = BTreeMap<&'static str, Box)->f64 + 'a>>; impl EvalNamespace for StrToCallbackNamespace<'_> { #[inline] fn lookup(&mut self, name:&str, args:Vec, _keybuf:&mut String) -> Option { if let Some(f) = self.get_mut(name) { Some(f(args)) } else { None } } } /// Type alias for `Vec>` pub type LayeredStringToF64Namespace = Vec>; impl EvalNamespace for LayeredStringToF64Namespace { #[inline] fn lookup(&mut self, name:&str, args:Vec, keybuf:&mut String) -> Option { let key = key_from_nameargs(keybuf, name, &args); for map in self.iter().rev() { if let Some(&val) = map.get(key) { return Some(val); } } None } } // I'm not making a type alias for this because of the un-name-ability of closures: impl EvalNamespace for F where F:FnMut(&str,Vec)->Option { #[inline] fn lookup(&mut self, name:&str, args:Vec, _keybuf:&mut String) -> Option { self(name,args) } } impl EvalNamespace for EmptyNamespace { /// Always returns `None`, indicating that the variable is undefined. #[inline] fn lookup(&mut self, _name:&str, _args:Vec, _keybuf:&mut String) -> Option { None } } impl EvalNamespace for CachedCallbackNamespace<'_> { /// Returns a cached value if possible, otherwise delegates to the callback function. fn lookup(&mut self, name:&str, args:Vec, keybuf:&mut String) -> Option { let key = key_from_nameargs(keybuf, name, &args); if let Some(&val) = self.cache.get(key) { return Some(val); } match (self.cb)(name,args) { Some(val) => { self.cache.insert(key.to_string(),val); Some(val) } None => None, } } } impl Cached for CachedCallbackNamespace<'_> { fn cache_create(&mut self, name:String, val:f64) -> Result<(),Error> { if self.cache.contains_key(&name) { return Err(Error::AlreadyExists); } self.cache.insert(name, val); Ok(()) } fn cache_set(&mut self, name:String, val:f64) { self.cache.insert(name, val); } fn cache_clear(&mut self) { self.cache = BTreeMap::new(); } } impl<'a> CachedCallbackNamespace<'a> { #[inline] pub fn new(cb:F) -> Self where F:FnMut(&str,Vec)->Option + 'a { CachedCallbackNamespace{ cache:BTreeMap::new(), cb :Box::new(cb), } } } //// I am not ready to make this part of the public API yet. // impl EvalNamespace for CachedLayeredNamespace<'_> { // fn lookup(&mut self, name:&str, args:Vec, keybuf:&mut String) -> Option { // let key = key_from_nameargs(keybuf, name, &args); // // for map in self.caches.iter().rev() { // if let Some(&val) = map.get(key) { return Some(val); } // } // // match (self.cb)(name,args) { // Some(val) => { // // I'm using this panic-free 'match' structure for performance: // match self.caches.last_mut() { // Some(m_ref) => { m_ref.insert(key.to_string(),val); } // None => (), // unreachable // } // Some(val) // } // None => None, // } // } // fn cache_create(&mut self, name:String, val:f64) -> Result<(),Error> { // match self.caches.last_mut() { // Some(cur_layer) => { // if cur_layer.contains_key(&name) { return Err(Error::AlreadyExists); } // cur_layer.insert(name, val); // } // None => return Err(Error::Unreachable), // }; // Ok(()) // } // fn cache_set(&mut self, name:String, val:f64) { // match self.caches.last_mut() { // Some(m_ref) => { m_ref.insert(name, val); } // None => (), // unreachable // } // } // fn cache_clear(&mut self) { // self.caches = Vec::with_capacity(self.caches.len()); // Assume the future usage will be similar to historical usage. // self.push(); // } // } // impl Layered for CachedLayeredNamespace<'_> { // #[inline] // fn push(&mut self) { // self.caches.push(BTreeMap::new()); // } // #[inline] // fn pop(&mut self) { // self.caches.pop(); // } // } // impl<'a> CachedLayeredNamespace<'a> { // #[inline] // pub fn new(cb:F) -> Self where F:FnMut(&str,Vec)->Option + 'a { // let mut ns = CachedLayeredNamespace{ // caches:Vec::with_capacity(2), // cb :Box::new(cb), // }; // ns.push(); // ns // } // } //// I am not ready to make this part of the public API yet. // impl Bubble<'_,NS> where NS:EvalNamespace+Layered { // pub fn new<'a>(ns:&'a mut NS) -> Bubble<'a,NS> { // Bubble{ // ns, // count:0, // } // } // } // impl Drop for Bubble<'_,NS> where NS:EvalNamespace+Layered { // fn drop(&mut self) { // while self.count>0 { // self.pop(); // } // } // } // impl EvalNamespace for Bubble<'_,NS> where NS:EvalNamespace+Layered { // #[inline] // fn lookup(&mut self, name:&str, args:Vec, keybuf:&mut String) -> Option { // self.ns.lookup(name,args,keybuf) // } // #[inline] // fn cache_create(&mut self, name:String, val:f64) -> Result<(),Error> { // self.ns.cache_create(name,val) // } // #[inline] // fn cache_set(&mut self, name:String, val:f64) { // self.ns.cache_set(name,val) // } // #[inline] // fn cache_clear(&mut self) { // self.ns.cache_clear() // } // } // impl Layered for Bubble<'_,NS> where NS:EvalNamespace+Layered { // #[inline] // fn push(&mut self) { // self.ns.push(); // self.count = self.count+1; // } // #[inline] // fn pop(&mut self) { // if self.count>0 { // self.count = self.count+1; // self.ns.pop(); // } // } // } //// Commented out until we start using a layered namespace again. // #[cfg(test)] // mod internal_tests { // use super::*; // // #[test] // fn bubble() { // let mut ns = CachedLayeredNamespace::new(|_,_| None); // assert_eq!(ns.caches.len(), 1); // { // let mut bub = Bubble::new(&mut ns); bub.push(); // assert_eq!(bub.ns.caches.len(), 2); // bub.push(); // assert_eq!(bub.ns.caches.len(), 3); // bub.push(); // assert_eq!(bub.ns.caches.len(), 4); // bub.pop(); // assert_eq!(bub.ns.caches.len(), 3); // } // assert_eq!(ns.caches.len(), 1); // } // } fasteval-0.2.4/src/ez.rs010066400017500001750000000037061361005474200133300ustar0000000000000000//! An easy API for single-function-call expression evaluation. use crate::error::Error; use crate::parser::Parser; use crate::evaler::Evaler; use crate::slab::Slab; use crate::evalns::EvalNamespace; /// The `ez_eval()` function provides a very simple way to perform expression evaluation with just one function call. /// /// If you only need to evaluate an expression one time, then `ez_eval()` will /// probably be perfectly adequate. But if you plan to evaluate the same /// expression many times, or if you plan to evaluate many expressions, you /// are able to achieve better performance by allocating a single `Slab` and /// using it to perform multiple parse-compile-eval cycles yourself. /// /// # Errors /// /// If there are any [`Error`](../error/enum.Error.html)s during the parse-eval process, they will be returned. /// /// # Examples /// /// [See the `fasteval` top-level documentation for examples.](../index.html#easy-evaluation) pub fn ez_eval(expr_str:&str, ns:&mut impl EvalNamespace) -> Result { let mut slab = Slab::new(); // A big block of memory, so we don't need to perform many tiny (and slow!) allocations. // Here is a one-liner that performs the entire parse-and-eval process: // Parser::new().parse(expr_str, &mut slab.ps)?.from(&slab.ps).eval(&mut slab, &mut ns) // Here is the same process, broken into steps: // First, parse the string: // We use the 'parse_noclear' function instead of 'parse' because we know the slab is empty. let expr_i = Parser::new().parse_noclear(expr_str, &mut slab.ps)?; // 'expr_i' is an index into the Slab. You can extract the Expression object with either of these: // slab.get_expr(expr_i) ...OR... expr_i.from(&slab.ps) // The first is more direct. The second is a convenience built on top of the first. let expr_ref = slab.ps.get_expr(expr_i); // Use the reference to the Expression object to perform the evaluation: expr_ref.eval(&slab, ns) } fasteval-0.2.4/src/lib.rs010066400017500001750000000742471361310112500134570ustar0000000000000000//! Fast evaluation of algebraic expressions //! //! # Features //! * No dependencies. //! * Safe execution of untrusted expressions. //! * Works with stable Rust. //! * Supports interpretation (i.e. parse & eval) as well as compiled execution (i.e. parse, compile, eval). //! * Supports Variables and Custom Functions. //! * `fasteval` is a good base for building higher-level languages. //! * Supports many built-in functions and constants. //! * Supports all the standard algebraic unary and binary operators (+ - * / ^ %), //! as well as comparisons (< <= == != >= >) and logical operators (&& ||) with //! short-circuit support. //! * Easy integration into many different types of applications, including scoped evaluation. //! * Very fast performance. //! //! # The `fasteval` Expression "Mini-Language" //! //! ## Built-in Functions and Constants //! //! These are the built-in functions that `fasteval` expressions support. (You //! can also add your own custom functions and variables -- see the //! [Examples](#advanced-variables-and-custom-functions) section.) //! //! ```text //! * print(...strings and values...) -- Prints to stderr. Very useful to 'probe' an expression. //! Evaluates to the last value. //! Example: `print("x is", x, "and y is", y)` //! Example: `x + print("y:", y) + z == x+y+z` //! //! * log(base=10, val) -- Logarithm with optional 'base' as first argument. //! If not provided, 'base' defaults to '10'. //! Example: `log(100) + log(e(), 100)` //! //! * e() -- Euler's number (2.718281828459045) //! * pi() -- π (3.141592653589793) //! //! * int(val) //! * ceil(val) //! * floor(val) //! * round(modulus=1, val) -- Round with optional 'modulus' as first argument. //! Example: `round(1.23456) == 1 && round(0.001, 1.23456) == 1.235` //! //! * abs(val) //! * sign(val) //! //! * min(val, ...) -- Example: `min(1, -2, 3, -4) == -4` //! * max(val, ...) -- Example: `max(1, -2, 3, -4) == 3` //! //! * sin(radians) * asin(val) //! * cos(radians) * acos(val) //! * tan(radians) * atan(val) //! * sinh(val) * asinh(val) //! * cosh(val) * acosh(val) //! * tanh(val) * atanh(val) //! ``` //! //! ## Operators //! //! The `and` and `or` operators are enabled by default, but if your //! application wants to use those words for something else, they can be //! disabled by turning off the `alpha-keywords` feature (`cargo build --no-default-features`). //! //! ```text //! Listed in order of precedence: //! //! (Highest Precedence) ^ Exponentiation //! % Modulo //! / Division //! * Multiplication //! - Subtraction //! + Addition //! == != < <= >= > Comparisons (all have equal precedence) //! && and Logical AND with short-circuit //! (Lowest Precedence) || or Logical OR with short-circuit //! //! ``` //! //! ## Numeric Literals //! //! ```text //! Several numeric formats are supported: //! //! Integers: 1, 2, 10, 100, 1001 //! //! Decimals: 1.0, 1.23456, 0.000001 //! //! Exponents: 1e3, 1E3, 1e-3, 1E-3, 1.2345e100 //! //! Suffix: //! 1.23p = 0.00000000000123 //! 1.23n = 0.00000000123 //! 1.23µ, 1.23u = 0.00000123 //! 1.23m = 0.00123 //! 1.23K, 1.23k = 1230 //! 1.23M = 1230000 //! 1.23G = 1230000000 //! 1.23T = 1230000000000 //! ``` //! //! # Examples //! //! ## Easy evaluation //! The [`ez_eval()`](ez/fn.ez_eval.html) function performs the entire allocation-parse-eval process //! for you. It is slightly inefficient because it always allocates a //! fresh [`Slab`](slab/index.html), but it is very simple to use: //! //! ``` //! // In case you didn't know, Rust allows `main()` to return a `Result`. //! // This lets us use the `?` operator inside of `main()`. Very convenient! //! fn main() -> Result<(), fasteval::Error> { //! // This example doesn't use any variables, so just use an EmptyNamespace: //! let mut ns = fasteval::EmptyNamespace; //! //! let val = fasteval::ez_eval( //! "1+2*3/4^5%6 + log(100K) + log(e(),100) + [3*(3-3)/3] + (2<3) && 1.23", &mut ns)?; //! // | | | | | | | | //! // | | | | | | | boolean logic with short-circuit support //! // | | | | | | comparisons //! // | | | | | square-brackets act like parenthesis //! // | | | | built-in constants: e(), pi() //! // | | | 'log' can take an optional first 'base' argument, defaults to 10 //! // | | numeric literal with suffix: p, n, µ, m, K, M, G, T //! // | many built-in functions: print, int, ceil, floor, abs, sign, log, round, min, max, sin, asin, ... //! // standard binary operators //! //! assert_eq!(val, 1.23); //! //! Ok(()) //! } //! ``` //! //! //! ## Simple variables //! Several namespace types are supported, each designed for different situations. //! ([See the various Namespace types here.](evalns/index.html)) For simple cases, you can define variables with a //! [`BTreeMap`](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html): //! //! ``` //! use std::collections::BTreeMap; //! fn main() -> Result<(), fasteval::Error> { //! let mut map : BTreeMap = BTreeMap::new(); //! map.insert("x".to_string(), 1.0); //! map.insert("y".to_string(), 2.0); //! map.insert("z".to_string(), 3.0); //! //! let val = fasteval::ez_eval(r#"x + print("y:",y) + z"#, &mut map)?; //! // | //! // prints "y: 2" to stderr and then evaluates to 2.0 //! //! assert_eq!(val, 6.0); //! //! Ok(()) //! } //! ``` //! //! ## Advanced variables and custom functions //! This time, instead of using a map, we will use a callback function, //! which defines custom variables, functions, and array-like objects: //! //! ``` //! fn main() -> Result<(), fasteval::Error> { //! let mut cb = |name:&str, args:Vec| -> Option { //! let mydata : [f64; 3] = [11.1, 22.2, 33.3]; //! match name { //! // Custom constants/variables: //! "x" => Some(3.0), //! "y" => Some(4.0), //! //! // Custom function: //! "sum" => Some(args.into_iter().sum()), //! //! // Custom array-like objects: //! // The `args.get...` code is the same as: //! // mydata[args[0] as usize] //! // ...but it won't panic if either index is out-of-bounds. //! "data" => args.get(0).and_then(|f| mydata.get(*f as usize).copied()), //! //! // A wildcard to handle all undefined names: //! _ => None, //! } //! }; //! //! let val = fasteval::ez_eval("sum(x^2, y^2)^0.5 + data[0]", &mut cb)?; //! // | | | //! // | | square-brackets act like parenthesis //! // | variables are like custom functions with zero args //! // custom function //! //! assert_eq!(val, 16.1); //! //! // Let's explore some of the hidden complexities of variables: //! // //! // * There's really no difference between a variable and a custom function. //! // Therefore, variables can receive arguments too, //! // which will probably be ignored. //! // Therefore, these two expressions evaluate to the same thing: //! // eval("x + y") == eval("x(1,2,3) + y(x, y, sum(x,y))") //! // ^^^^^ ^^^^^^^^^^^^^^ //! // All this stuff is ignored. //! // //! // * Built-in functions take precedence WHEN CALLED AS FUNCTIONS. //! // This design was chosen so that builtin functions do not pollute //! // the variable namespace, which is important for some applications. //! // Here are some examples: //! // pi -- Uses the custom 'pi' variable, NOT the builtin 'pi' function. //! // pi() -- Uses the builtin 'pi' function even if a custom variable is defined. //! // pi(1,2,3) -- Uses the builtin 'pi' function, and produces a WrongArgs error //! // during parse because the builtin does not expect any arguments. //! // x -- Uses the custom 'x' variable. //! // x() -- Uses the custom 'x' variable because there is no 'x' builtin. //! // x(1,2,3) -- Uses the custom 'x' variable. The args are ignored. //! // sum -- Uses the custom 'sum' function with no arguments. //! // sum() -- Uses the custom 'sum' function with no arguments. //! // sum(1,2) -- Uses the custom 'sum' function with two arguments. //! //! Ok(()) //! } //! ``` //! //! ## Re-use the Slab to go faster //! If we perform the parse and eval ourselves (without relying on the 'ez' //! interface), then we can re-use the [`Slab`](slab/index.html) allocation for //! subsequent parsing and evaluations. This avoids a significant amount of //! slow memory operations: //! //! ``` //! use std::collections::BTreeMap; //! use fasteval::Evaler; // use this trait so we can call eval(). //! fn main() -> Result<(), fasteval::Error> { //! let parser = fasteval::Parser::new(); //! let mut slab = fasteval::Slab::new(); //! //! // See the `parse` documentation to understand why we use `from` like this: //! let expr_ref = parser.parse("x + 1", &mut slab.ps)?.from(&slab.ps); //! //! // Let's evaluate the expression a couple times with different 'x' values: //! //! let mut map : BTreeMap = BTreeMap::new(); //! map.insert("x".to_string(), 1.0); //! let val = expr_ref.eval(&slab, &mut map)?; //! assert_eq!(val, 2.0); //! //! map.insert("x".to_string(), 2.5); //! let val = expr_ref.eval(&slab, &mut map)?; //! assert_eq!(val, 3.5); //! //! // Now, let's re-use the Slab for a new expression. //! // (This is much cheaper than allocating a new Slab.) //! // The Slab gets cleared by 'parse()', so you must avoid using //! // the old expr_ref after parsing the new expression. //! // One simple way to avoid this problem is to shadow the old variable: //! //! let expr_ref = parser.parse("x * 10", &mut slab.ps)?.from(&slab.ps); //! //! let val = expr_ref.eval(&slab, &mut map)?; //! assert_eq!(val, 25.0); //! //! Ok(()) //! } //! ``` //! //! ## Compile to go super fast! //! If you plan to evaluate an expression just one or two times, then you //! should parse-eval as shown in previous examples. But if you expect to //! evaluate an expression three or more times, you can dramatically improve //! your performance by compiling. The compiled form is usually more than 10 //! times faster than the un-compiled form, and for constant expressions it is //! usually more than 200 times faster. //! ``` //! use std::collections::BTreeMap; //! use fasteval::Evaler; // use this trait so we can call eval(). //! use fasteval::Compiler; // use this trait so we can call compile(). //! fn main() -> Result<(), fasteval::Error> { //! let parser = fasteval::Parser::new(); //! let mut slab = fasteval::Slab::new(); //! let mut map = BTreeMap::new(); //! //! let expr_str = "sin(deg/360 * 2*pi())"; //! let compiled = parser.parse(expr_str, &mut slab.ps)?.from(&slab.ps).compile(&slab.ps, &mut slab.cs); //! for deg in 0..360 { //! map.insert("deg".to_string(), deg as f64); //! // When working with compiled constant expressions, you can use the //! // eval_compiled*!() macros to save a function call: //! let val = fasteval::eval_compiled!(compiled, &slab, &mut map); //! eprintln!("sin({}°) = {}", deg, val); //! } //! //! Ok(()) //! } //! ``` //! //! ## Unsafe Variables //! If your variables *must* be as fast as possible and you are willing to be //! very careful, you can build with the `unsafe-vars` feature (`cargo build //! --features unsafe-vars`), which enables pointer-based variables. These //! unsafe variables perform 2x-4x faster than the compiled form above. This //! feature is not enabled by default because it slightly slows down other //! non-variable operations. //! ``` //! use fasteval::Evaler; // use this trait so we can call eval(). //! use fasteval::Compiler; // use this trait so we can call compile(). //! fn main() -> Result<(), fasteval::Error> { //! let parser = fasteval::Parser::new(); //! let mut slab = fasteval::Slab::new(); //! //! // The Unsafe Variable will use a pointer to read this memory location: //! // You must make sure that this variable stays in-scope as long as the //! // expression is in-use. //! let mut deg : f64 = 0.0; //! //! // Unsafe Variables must be registered before 'parse()'. //! // (Normal Variables only need definitions during the 'eval' phase.) //! unsafe { slab.ps.add_unsafe_var("deg".to_string(), °); } // `add_unsafe_var()` only exists if the `unsafe-vars` feature is enabled: `cargo test --features unsafe-vars` //! //! let expr_str = "sin(deg/360 * 2*pi())"; //! let compiled = parser.parse(expr_str, &mut slab.ps)?.from(&slab.ps).compile(&slab.ps, &mut slab.cs); //! //! let mut ns = fasteval::EmptyNamespace; // We only define unsafe variables, not normal variables, //! // so EmptyNamespace is fine. //! //! for d in 0..360 { //! deg = d as f64; //! let val = fasteval::eval_compiled!(compiled, &slab, &mut ns); //! eprintln!("sin({}°) = {}", deg, val); //! } //! //! Ok(()) //! } //! ``` //! //! ## Let's Develop an Intuition of `fasteval` Internals //! In this advanced example, we peek into the Slab to see how expressions are //! represented after the 'parse' and 'compile' phases. //! ``` //! use fasteval::Compiler; // use this trait so we can call compile(). //! fn main() -> Result<(), fasteval::Error> { //! let parser = fasteval::Parser::new(); //! let mut slab = fasteval::Slab::new(); //! //! let expr_str = "sin(deg/360 * 2*pi())"; //! let expr_ref = parser.parse(expr_str, &mut slab.ps)?.from(&slab.ps); //! //! // Let's take a look at the parsed AST inside the Slab: //! // If you find this structure confusing, take a look at the compilation //! // AST below because it is simpler. //! assert_eq!(format!("{:?}", slab.ps), //! r#"ParseSlab{ exprs:{ 0:Expression { first: EStdFunc(EVar("deg")), pairs: [ExprPair(EDiv, EConstant(360.0)), ExprPair(EMul, EConstant(2.0)), ExprPair(EMul, EStdFunc(EFuncPi))] }, 1:Expression { first: EStdFunc(EFuncSin(ExpressionI(0))), pairs: [] } }, vals:{} }"#); //! // Pretty-Print: //! // ParseSlab{ //! // exprs:{ //! // 0:Expression { first: EStdFunc(EVar("deg")), //! // pairs: [ExprPair(EDiv, EConstant(360.0)), //! // ExprPair(EMul, EConstant(2.0)), //! // ExprPair(EMul, EStdFunc(EFuncPi))] //! // }, //! // 1:Expression { first: EStdFunc(EFuncSin(ExpressionI(0))), //! // pairs: [] } //! // }, //! // vals:{} //! // } //! //! let compiled = expr_ref.compile(&slab.ps, &mut slab.cs); //! //! // Let's take a look at the compilation results and the AST inside the Slab: //! // Notice that compilation has performed constant-folding: 1/360 * 2*pi = 0.017453292519943295 //! // In the results below: IFuncSin(...) represents the sin function. //! // InstructionI(1) represents the Instruction stored at index 1. //! // IMul(...) represents the multiplication operator. //! // 'C(0.017...)' represents a constant value of 0.017... . //! // IVar("deg") represents a variable named "deg". //! assert_eq!(format!("{:?}", compiled), //! "IFuncSin(InstructionI(1))"); //! assert_eq!(format!("{:?}", slab.cs), //! r#"CompileSlab{ instrs:{ 0:IVar("deg"), 1:IMul(InstructionI(0), C(0.017453292519943295)) } }"#); //! //! Ok(()) //! } //! ``` //! //! # Safety //! //! `fasteval` is designed to evaluate untrusted expressions safely. By //! default, an expression can only perform math operations; there is no way //! for it to access other types of operations (like network or filesystem or //! external commands). Additionally, we guard against malicious expressions: //! //! * Expressions that are too large (greater than 4KB). //! * Expressions that are too-deeply nested (greater than 32 levels). //! * Expressions with too many values (greater than 64). //! * Expressions with too many sub-expressions (greater than 64). //! //! All limits can be customized at parse time. If any limits are exceeded, //! [`parse()`](https://docs.rs/fasteval/latest/fasteval/parser/struct.Parser.html#method.parse) will return an //! [Error](https://docs.rs/fasteval/latest/fasteval/error/enum.Error.html). //! //! Note that it *is* possible for you (the developer) to define custom functions //! which might perform dangerous operations. It is your responsibility to make //! sure that all custom functionality is safe. //! //! //! # Performance Benchmarks //! //! These benchmarks were performed on 2019-12-25. Merry Christmas. //! //! Here are links to all the libraries/tools included in these benchmarks: //! //! * [fasteval (this library)](https://github.com/likebike/fasteval) //! * [caldyn](https://github.com/Luthaf/caldyn) //! * [rsc](https://github.com/codemessiah/rsc) //! * [meval](https://github.com/rekka/meval-rs) //! * [calc](https://github.com/redox-os/calc) //! * [tinyexpr (Rust)](https://github.com/kondrak/tinyexpr-rs) //! * [tinyexpr (C)](https://github.com/codeplea/tinyexpr) //! * [bc](https://www.gnu.org/software/bc/) //! * [python3](https://www.python.org/) //! //! ## Charts //! Note that the following charts use logarithmic scales. Therefore, tiny //! visual differences actually represent very significant performance //! differences. //! //! **Performance of evaluation of a compiled expression:** //! ![Compiled Eval Performance](https://raw.githubusercontent.com/likebike/fasteval/master/benches/results/20191225/fasteval-compiled.png) //! //! **Performance of one-time interpretation (parse and eval):** //! ![Interpretation Performance](https://raw.githubusercontent.com/likebike/fasteval/master/benches/results/20191225/fasteval-interp.png) //! //! **Performance of compiled Unsafe Variables, compared to the tinyexpr C library (the //! only other library in our test set that supports this mode):** //! ![Unsafe Compiled Eval Performance](https://raw.githubusercontent.com/likebike/fasteval/master/benches/results/20191225/fasteval-compiled-unsafe.png) //! //! **Performance of interpreted Unsafe Variables, compared to the tinyexpr C library (the //! only other library in our test set that supports this mode):** //! ![Unsafe Interpretation Performance](https://raw.githubusercontent.com/likebike/fasteval/master/benches/results/20191225/fasteval-interp-unsafe.png) //! //! ## Summary //! //! The impressive thing about these results is that `fasteval` consistently //! achieves the fastest times across every benchmark and in every mode of //! operation (interpreted, compiled, and unsafe). It's easy to create a //! design to claim the #1 spot in any one of these metrics by sacrificing //! performance in another, but it is difficult to create a design that can be //! #1 across-the-board. //! //! Because of the broad and robust performance advantages, `fasteval` is very //! likely to be an excellent choice for your dynamic evaluation needs. //! //! ## Benchmark Descriptions & Analysis //! ```text //! * simple = `3 * 3 - 3 / 3` //! This is a simple test with primitive binary operators. //! Since the expression is quite simple, it does a good job of showing //! the intrinsic performance costs of a library. //! Results: //! * For compiled expressions, `fasteval` is 6x as fast as the closest //! competitor (caldyn) because the `eval_compiled!()` macro is able to //! eliminate all function calls. If the macro is not used and a //! normal `expr.eval()` function call is performed instead, then //! performance is very similar to caldyn's. //! * For interpreted expressions, `fasteval` is 2x as fast as the //! tinyexpr C lib, and 3x as fast as the tinyexpr Rust lib. //! This is because `fasteval` eliminates redundant work and memory //! allocation during the parse phase. //! //! * power = `2 ^ 3 ^ 4` //! `2 ^ (3 ^ 4)` for `tinyexpr` and `rsc` //! This test shows the associativity of the exponent operator. //! Most libraries (including `fasteval`) use right-associativity, //! but some libraries (particularly tinyexpr and rsc) use //! left-associativity. //! This test is also interesting because it shows the precision of a //! library's number system. `fasteval` just uses f64 and therefore truncates //! the result (2417851639229258300000000), while python, bc, and the //! tinyexpr C library produce a higher precision result //! (2417851639229258349412352). //! Results: //! Same as the 'simple' case. //! //! * variable = `x * 2` //! This is a simple test of variable support. //! Since the expression is quite simple, it shows the intrinsic //! performance costs of a library's variables. //! Results: //! * The tinyexpr Rust library does not currently support variables. //! * For safe compiled evaluation, `fasteval` is 4.4x as fast as the closest //! competitor (caldyn). //! * For safe interpretation, `fasteval` is 3.3x as fast as the closest //! competitor (caldyn). //! * For unsafe variables, `fasteval` is 1.2x as fast as the //! tinyexpr C library. //! //! * trig = `sin(x)` //! This is a test of variables, built-in function calls, and trigonometry. //! Results: //! * The tinyexpr Rust library does not currently support variables. //! * The `calc` library does not support trigonometry. //! * For safe compiled evaluation, `fasteval` is 2.6x as fast as the //! closest competitor (caldyn). //! * For safe interpretation, `fasteval` is 2.3x as fast as the closest //! competitor (caldyn). //! * Comparing unsafe variables with the tinyexpr C library, //! `fasteval` is 8% slower for compiled expressions (tinyexpr uses a //! faster `sin` implementation) and 4% faster for interpreted //! expressions (`fasteval` performs less memory allocation). //! //! * quadratic = `(-z + (z^2 - 4*x*y)^0.5) / (2*x)` //! This test demonstrates a more complex expression, involving several //! variables, some of which are accessed more than once. //! Results: //! * The tinyexpr Rust library does not currently support variables. //! * For safe compiled evaluation, `fasteval` is 2x as fast as the //! closest competitor (rsc). //! * For safe interpretation, `fasteval` is 3.7x as fast as the //! closest competitor (caldyn). //! * Comparing unsafe variables with the tinyexpr C library, //! `fasteval` is the same speed for compiled expressions, //! and 1.2x as fast for interpretation. //! //! * large = `((((87))) - 73) + (97 + (((15 / 55 * ((31)) + 35))) + (15 - (9)) - (39 / 26) / 20 / 91 + 27 / (33 * 26 + 28 - (7) / 10 + 66 * 6) + 60 / 35 - ((29) - (69) / 44 / (92)) / (89) + 2 + 87 / 47 * ((2)) * 83 / 98 * 42 / (((67)) * ((97))) / (34 / 89 + 77) - 29 + 70 * (20)) + ((((((92))) + 23 * (98) / (95) + (((99) * (41))) + (5 + 41) + 10) - (36) / (6 + 80 * 52 + (90))))` //! This is a fairly large expression that highlights parsing costs. //! Results: //! * Since there are no variables in the expression, `fasteval` and //! `caldyn` compile this down to a single constant value. That's //! why these two libraries are so much faster than the rest. //! * For compiled evaluation, `fasteval` is 6x as fast as `caldyn` //! because it is able to eliminate function calls with the //! `eval_compiled!()` macro. //! * For interpretation, `fasteval` is 2x as fast as the closest //! competitor (rsc). //! * Comparing unsafe variables with the tinyexpr C library, //! `fasteval` is 3x as fast for compiled evaluation, and //! 1.2x as fast for interpretation. //! ``` //! //! ## Methodology //! I am running Ubuntu 18.04 on an Asus G55V (a 2012 laptop with Intel Core i7-3610QM CPU @ 2.3GHz - 3.3GHz). //! //! All numeric results can be found in `fasteval/benches/bench.rs`. //! //! See the [detailed post about my benchmarking methology]{http://likebike.com/posts/How_To_Write_Fast_Rust_Code.html#how-to-measure} //! on my blog. //! //! # How is `fasteval` so fast? //! //! A variety of techniques are used to optimize performance: //! * [Minimization of memory allocations/deallocations](http://likebike.com/posts/How_To_Write_Fast_Rust_Code.html#reduce-redundancy-mem); //! I just pre-allocate a large `Slab` during initialization. //! * Elimination of redundant work, [especially when parsing](http://likebike.com/posts/How_To_Write_Fast_Rust_Code.html#reduce-redundancy-logic). //! * Designed using ["Infallible Data Structures"](http://likebike.com/posts/How_To_Write_Fast_Rust_Code.html#reduce-redundancy-data), which eliminate all corner cases. //! * Compilation: Constant Folding and Expression Simplification. //! Boosts performance up to 1000x. //! * Profile-driven application of inlining. Don't inline too much or too little. //! Maximizes data locality. //! * Localize variables. Use [`RUSTFLAGS="--emit=asm"`](http://likebike.com/posts/How_To_Write_Fast_Rust_Code.html#emit-asm) as a guide. //! //! # Can `fasteval` be faster? //! //! Yes, but not easily, and not by much. //! //! To boost the 'eval' phase, we would really need to perform compilation to //! machine code, which is difficult and non-portable across platforms, and //! increases the likelyhood of security vulnerabilities. Also, the potential //! gains are limited: We already run at //! half-the-speed-of-compiled-optimized-Rust for constant expressions (the //! most common case). So for constant expressions, the most you could gain //! from compilation-to-machine-code is a 2x performance boost. We are already //! operating close to the theoretical limit! //! //! It is possible to perform faster evaluation of non-constant expressions by //! introducing more constraints or complexity: //! * If I introduce a 'const' var type, then I can transform variable //! expressions into constant expressions. I don't think this would be //! useful-enough in real-life to justify the extra complexity (but please //! tell me if your use-case would benefit from this). //! * Evaluation could be paralellized (with a more complex design). //! //! It is possible to boost overall speed by improving the parsing algorithm //! to produce a Reverse Polish Notation AST directly, rather than the currennt //! infix AST which is then converted to RPN during compilation. However, this //! isn't as simple as just copying the Shunting-Yard algorithm because I //! support more advanced (and customizable) syntax (such as function calls and //! strings), while Shunting-Yard is designed only for algebraic expressions. //! //! //! # Future Work //! Here are some features that I might add in the future: //! //! * Dynamic `sprintf` string formatting for the `print()` built-in expression function. //! * FFI so this library can be used from other languages. //! * Ability to copy the contents of a `Slab` into a perfectly-sized container //! (`PackedSlab`) to reduce wasted memory. //! * Support for other number types other than `f64`, such as Integers, Big Integers, //! Arbitrary Precision Numbers, Complex Numbers, etc. like [rclc](https://crates.io/crates/rclc). //! //! # List of Projects that use `fasteval` //! //! [Send me a message](mailto:christopher@likebike.com) if you would like to list your project here. //! //! * [koin.cx](http://koin.cx/) //! * [robit](#coming-soon) //! * [openpinescript](#coming-soon) //! * [The Texas Instruments MW-83 Plus Scientific Microwave Oven](https://raw.githubusercontent.com/likebike/fasteval/master/examples/scientific-microwave-ti-mw-83-plus.jpg) //#![feature(test)] //#![warn(missing_docs)] //// Keeping for reference: // #![cfg_attr(feature="nightly", feature(slice_index_methods))] pub mod error; #[macro_use] pub mod slab; pub mod parser; #[macro_use] pub mod compiler; pub mod evaler; pub mod evalns; pub mod ez; pub use self::error::Error; pub use self::parser::{Parser, Expression, ExpressionI, Value, ValueI}; pub use self::compiler::{Compiler, Instruction::{self, IConst}, InstructionI}; #[cfg(feature="unsafe-vars")] pub use self::compiler::Instruction::IUnsafeVar; pub use self::evaler::Evaler; pub use self::slab::Slab; pub use self::evalns::{EvalNamespace, Cached, EmptyNamespace, StringToF64Namespace, StrToF64Namespace, StringToCallbackNamespace, StrToCallbackNamespace, LayeredStringToF64Namespace, CachedCallbackNamespace}; pub use self::ez::ez_eval; // TODO: Convert `match`es to `if let`s for performance boost. fasteval-0.2.4/src/parser.rs010066400017500001750000001201761361006044700142060ustar0000000000000000//! This module parses string expressions into an AST which can then be compiled or evaluated. //! //! # fasteval Algebra Grammar //! ```text //! Expression: Value (BinaryOp Value)* //! //! Value: Constant || UnaryOp || PrintFunc || StdFunc //! //! Constant: [+-]?[0-9]*(\.[0-9]+)?( ([eE][+-]?[0-9]+) || [pnuµmkKMGT] )? || [+-]?(NaN || inf) //! //! UnaryOp: +Value || -Value || (Expression) || [Expression] || !Value //! //! BinaryOp: + || - || * || / || % || ^ || < || <= || == || != || >= || > || (or || '||') || (and || '&&') //! //! VarName: [a-zA-Z_][a-zA-Z_0-9]* //! //! StdFunc: VarName((Expression,)*)? || VarName[(Expression,)*]? //! //! PrintFunc: print(ExpressionOrString,*) //! //! ExpressionOrString: Expression || String //! //! String: ".*" //! ``` use crate::error::Error; use crate::slab::ParseSlab; use std::str::{from_utf8, from_utf8_unchecked}; use std::ptr; /// An `ExpressionI` represents an index into `Slab.ps.exprs`. /// /// It behaves much like a pointer or reference, but it is 'safe' (unlike a raw /// pointer) and is not managed by the Rust borrow checker (unlike a reference). #[derive(Debug, PartialEq, Copy, Clone)] pub struct ExpressionI(pub usize); /// A `ValueI` represents an index into `Slab.ps.vals`. /// /// It behaves much like a pointer or reference, but it is 'safe' (unlike a raw /// pointer) and is not managed by the Rust borrow checker (unlike a reference). #[derive(Debug, PartialEq, Copy, Clone)] pub struct ValueI(pub usize); /// An `Expression` is the top node of a parsed AST. /// /// It can be `compile()`d or `eval()`d. #[derive(Debug, PartialEq)] pub struct Expression { pub(crate) first: Value, pub(crate) pairs: Vec, // cap=8 } #[derive(Debug, PartialEq)] pub(crate) struct ExprPair(pub BinaryOp, pub Value); /// A `Value` can be a Constant, a UnaryOp, a StdFunc, or a PrintFunc. #[derive(Debug, PartialEq)] pub enum Value { EConstant(f64), EUnaryOp(UnaryOp), EStdFunc(StdFunc), EPrintFunc(PrintFunc), } use Value::{EConstant, EUnaryOp, EStdFunc, EPrintFunc}; /// Unary Operators #[derive(Debug, PartialEq)] pub enum UnaryOp { EPos(ValueI), ENeg(ValueI), ENot(ValueI), EParentheses(ExpressionI), } use UnaryOp::{EPos, ENeg, ENot, EParentheses}; /// Binary Operators #[derive(Debug, PartialEq, PartialOrd, Copy, Clone)] pub enum BinaryOp { // Sorted in order of precedence (low-priority to high-priority): // Keep this order in-sync with evaler.rs. (Search for 'rtol' and 'ltor'.) EOR = 1, // Lowest Priority EAND = 2, ENE = 3, EEQ = 4, EGTE = 5, ELTE = 6, EGT = 7, ELT = 8, EAdd = 9, ESub = 10, EMul = 11, EDiv = 12, EMod = 13, EExp = 14, // Highest Priority } use BinaryOp::{EAdd, ESub, EMul, EDiv, EMod, EExp, ELT, ELTE, EEQ, ENE, EGTE, EGT, EOR, EAND}; /// A Function Call with Standard Syntax. #[derive(Debug, PartialEq)] pub enum StdFunc { EVar(String), #[cfg(feature="unsafe-vars")] EUnsafeVar{name:String, ptr:*const f64}, EFunc{name:String, args:Vec}, // cap=4 EFuncInt(ExpressionI), EFuncCeil(ExpressionI), EFuncFloor(ExpressionI), EFuncAbs(ExpressionI), EFuncSign(ExpressionI), EFuncLog{ base:Option, expr:ExpressionI}, EFuncRound{modulus:Option, expr:ExpressionI}, EFuncMin{first:ExpressionI, rest:Vec}, // cap=4 EFuncMax{first:ExpressionI, rest:Vec}, // cap=4 EFuncE, EFuncPi, EFuncSin(ExpressionI), EFuncCos(ExpressionI), EFuncTan(ExpressionI), EFuncASin(ExpressionI), EFuncACos(ExpressionI), EFuncATan(ExpressionI), EFuncSinH(ExpressionI), EFuncCosH(ExpressionI), EFuncTanH(ExpressionI), EFuncASinH(ExpressionI), EFuncACosH(ExpressionI), EFuncATanH(ExpressionI), } use StdFunc::{EVar, EFunc, EFuncInt, EFuncCeil, EFuncFloor, EFuncAbs, EFuncSign, EFuncLog, EFuncRound, EFuncMin, EFuncMax, EFuncE, EFuncPi, EFuncSin, EFuncCos, EFuncTan, EFuncASin, EFuncACos, EFuncATan, EFuncSinH, EFuncCosH, EFuncTanH, EFuncASinH, EFuncACosH, EFuncATanH}; #[cfg(feature="unsafe-vars")] use StdFunc::EUnsafeVar; /// Represents a `print()` function call in the `fasteval` expression AST. #[derive(Debug, PartialEq)] pub struct PrintFunc(pub Vec); // cap=8 /// Used by the `print()` function. Can hold an `Expression` or a `String`. #[derive(Debug, PartialEq)] pub enum ExpressionOrString { EExpr(ExpressionI), EStr(String), // cap=64 } use ExpressionOrString::{EExpr, EStr}; impl Clone for PrintFunc { fn clone(&self) -> Self { let mut vec = Vec::::with_capacity(self.0.len()); for x_or_s in self.0.iter() { vec.push(match x_or_s { EExpr(i) => EExpr(*i), EStr(s) => EStr(s.clone()), }); } PrintFunc(vec) } } enum Token { Pass, Bite(T), } use Token::{Pass, Bite}; macro_rules! peek { ($bs:ident) => { $bs.first().copied() }; } macro_rules! peek_n { ($bs:ident, $skip:literal) => { $bs.get($skip).copied() }; ($bs:ident, $skip:ident) => { $bs.get($skip).copied() }; ($bs:ident, $skip:expr) => { $bs.get($skip).copied() }; } macro_rules! peek_is { ($bs:ident, $skip:literal, $val:literal) => { peek_n!($bs,$skip) == Some($val) }; ($bs:ident, $skip:expr, $val:literal) => { peek_n!($bs,$skip) == Some($val) }; } macro_rules! read { ($bs:ident) => { match $bs.first() { Some(b) => { *$bs = &$bs[1..]; Ok(*b) } None => Err(Error::EOF), } }; ($bs:ident, $parsing:literal) => { match $bs.first() { Some(b) => { *$bs = &$bs[1..]; Ok(*b) } None => Err(Error::EofWhileParsing($parsing.to_string())), } }; } macro_rules! skip { ($bs:ident) => { *$bs = &$bs[1..]; }; } macro_rules! skip_n { ($bs:ident, $n:literal) => { *$bs = &$bs[$n..]; }; ($bs:ident, $n:ident) => { *$bs = &$bs[$n..]; }; } macro_rules! is_space { ($b:ident) => { if $b>b' ' { false } else { $b==b' ' || $b==b'\n' || $b==b'\t' || $b==b'\r' } }; } macro_rules! spaces { ($bs:ident) => { while let Some(b) = peek!($bs) { if !is_space!(b) { break } skip!($bs); // We normally don't have long strings of whitespace, so it is more efficient to put this single-skip inside this loop rather than a skip_n afterwards. } }; } pub const DEFAULT_EXPR_LEN_LIMIT : usize = 4096; pub const DEFAULT_EXPR_DEPTH_LIMIT: usize = 32; pub struct Parser { pub expr_len_limit :usize, pub expr_depth_limit:usize, } impl Parser { #[inline] pub const fn new() -> Self { Self{expr_len_limit:DEFAULT_EXPR_LEN_LIMIT, expr_depth_limit:DEFAULT_EXPR_DEPTH_LIMIT} } fn is_varname_byte(b:u8, i:usize) -> bool { (b'A'<=b && b<=b'Z') || (b'a'<=b && b<=b'z') || b==b'_' || (i>0 && ( b'0'<=b && b<=b'9' )) } fn is_varname_byte_opt(bo:Option, i:usize) -> bool { match bo { Some(b) => Self::is_varname_byte(b,i), None => false, } } /// Use this function to parse an expression String. The `Slab` will be cleared first. #[inline] pub fn parse(&self, expr_str:&str, slab:&mut ParseSlab) -> Result { slab.clear(); self.parse_noclear(expr_str, slab) } /// This is exactly the same as `parse()` but the `Slab` will NOT be cleared. /// This is useful in performance-critical sections, when you know that you /// already have an empty `Slab`. /// /// This function cannot return Result<&Expression> because it would /// prolong the mut ref. / That's why we return an ExpressionI instead. #[inline] pub fn parse_noclear(&self, expr_str:&str, slab:&mut ParseSlab) -> Result { if expr_str.len()>self.expr_len_limit { return Err(Error::TooLong); } // Restrict length for safety let mut bs = expr_str.as_bytes(); self.read_expression(slab, &mut bs, 0, true) } fn read_expression(&self, slab:&mut ParseSlab, bs:&mut &[u8], depth:usize, expect_eof:bool) -> Result { if depth>self.expr_depth_limit { return Err(Error::TooDeep); } let first = self.read_value(slab,bs,depth)?; let mut pairs = Vec::::with_capacity(8); loop { match self.read_binaryop(bs)? { Pass => break, Bite(bop) => { let val = self.read_value(slab,bs,depth)?; pairs.push(ExprPair(bop,val)); } } } spaces!(bs); if expect_eof && !bs.is_empty() { let bs_str = match from_utf8(bs) { Ok(s) => s, Err(..) => "Utf8Error while handling UnparsedTokensRemaining error", }; return Err(Error::UnparsedTokensRemaining(bs_str.to_string())); } Ok(slab.push_expr(Expression{first, pairs})?) } fn read_value(&self, slab:&mut ParseSlab, bs:&mut &[u8], depth:usize) -> Result { if depth>self.expr_depth_limit { return Err(Error::TooDeep) } match Self::read_const(slab,bs)? { Pass => {} Bite(c) => return Ok(EConstant(c)), } match self.read_unaryop(slab,bs,depth)? { Pass => {} Bite(u) => return Ok(EUnaryOp(u)), } match self.read_callable(slab,bs,depth)? { Pass => {} Bite(c) => return Ok(c), } // Improve the precision of this error case: if bs.is_empty() { return Err(Error::EofWhileParsing("value".to_string())); } Err(Error::InvalidValue) } fn read_const(slab:&mut ParseSlab, bs:&mut &[u8]) -> Result,Error> { spaces!(bs); let mut toklen=0; let mut sign_ok=true; let mut specials_ok=true; let mut suffix_ok=true; let mut saw_val=false; loop { match peek_n!(bs, toklen) { None => break, Some(b) => { if b'0'<=b && b<=b'9' || b==b'.' { saw_val = true; sign_ok=false; specials_ok=false; toklen = toklen+1; } else if sign_ok && (b==b'-' || b==b'+') { sign_ok = false; toklen = toklen+1; } else if saw_val && (b==b'e' || b==b'E') { suffix_ok = false; sign_ok = true; toklen = toklen+1; } else if specials_ok && ( b==b'N' && peek_is!(bs,toklen+1,b'a') && peek_is!(bs,toklen+2,b'N') || b==b'i' && peek_is!(bs,toklen+1,b'n') && peek_is!(bs,toklen+2,b'f') ) { #[cfg(feature="alpha-keywords")] { saw_val = true; suffix_ok = false; toklen = toklen+3; } break; } else { break; } } } } if !saw_val { return Ok(Pass); } let mut tok = unsafe { from_utf8_unchecked(&bs[..toklen]) }; if suffix_ok { match peek_n!(bs,toklen) { None => (), Some(b) => { let (exp,suffixlen) = match b { b'k' | b'K' => (3,1), b'M' => (6,1), b'G' => (9,1), b'T' => (12,1), b'm' => (-3,1), b'u' | b'\xb5' => (-6,1), // ASCII-encoded 'µ' b'\xc2' if peek_is!(bs,toklen+1,b'\xb5') => (-6,2), // UTF8-encoded 'µ' b'n' => (-9,1), b'p' => (-12,1), _ => (0,0), }; if exp!=0 { slab.char_buf.clear(); slab.char_buf.push_str(tok); slab.char_buf.push('e'); slab.char_buf.push_str(&exp.to_string()); tok = &slab.char_buf; toklen = toklen+suffixlen; } } } } let val = tok.parse::().map_err(|_| { Error::ParseF64(tok.to_string()) })?; skip_n!(bs,toklen); Ok(Bite(val)) } // // This implementation is beautiful and correct, but it is slow due to the fact that I am first parsing everything, // // and then I'm calling parse:: which repeats the entire process. // // I wish I could just call dec2flt::convert() ( https://doc.rust-lang.org/src/core/num/dec2flt/mod.rs.html#247 ) // // with all the pieces I already parsed, but, alas, that function is private. // // // // Also, I have decided that I really do need to support 'NaN' and 'inf'. I could add them below, but instead, // // I think I will just switch back to a drastically-simplified parser which isn't as "correct", but re-uses // // more functionality from the stdlib. // // // // As a side-note, It's surprising how similar these algorithms are (which I created from scratch at 3am with no reference), // // compared to the dec2flt::parse module. // fn read_const(&mut self, bs:&mut &[u8]) -> Result, KErr> { // spaces!(bs); // // // Grammar: [+-]?[0-9]*(\.[0-9]+)?( ([eE][+-]?[0-9]+) || [pnuµmkKMGT] )? // fn peek_digits(bs:&[u8]) -> usize { // let mut i = 0; // while i Result { // if bs.is_empty() { return Err(KErr::new("peek_exp empty")); } // let mut i = 0; // if bs[i]==b'-' || bs[i]==b'+' { i+=1; } // let digits = peek_digits(&bs[i..]); // if digits==0 { return Err(KErr::new("peek_exp no digits")); } // Ok(i+digits) // } // fn peek_tail(bs:&[u8]) -> Result<(/*read:*/usize, /*skip:*/usize, /*exp:*/i32), KErr> { // if bs.is_empty() { return Ok((0,0,0)); } // match bs[0] { // b'k' | b'K' => Ok((0,1,3)), // b'M' => Ok((0,1,6)), // b'G' => Ok((0,1,9)), // b'T' => Ok((0,1,12)), // b'm' => Ok((0,1,-3)), // b'u' | b'\xb5' => Ok((0,1,-6)), // ASCII-encoded 'µ' // b'\xc2' if bs.len()>1 && bs[1]==b'\xb5' => Ok((0,2,-6)), // UTF8-encoded 'µ' // b'n' => Ok((0,1,-9)), // b'p' => Ok((0,1,-12)), // b'e' | b'E' => peek_exp(&bs[1..]).map(|size| (1+size,0,0)), // _ => Ok((0,0,0)), // } // } // // let mut toread=0; let mut toskip=0; let mut exp=0; // // match peek(bs, 0) { // None => return Ok(Pass), // Some(b) => { // if b==b'-' || b==b'+' { toread+=1; } // } // // } // // let predec = peek_digits(&bs[toread..]); // toread+=predec; // // match peek(bs, toread) { // None => { // if predec==0 { return Ok(Pass); } // } // Some(b) => { // if b==b'.' { // toread+=1; // let postdec = peek_digits(&bs[toread..]); // if predec==0 && postdec==0 { return Err(KErr::new("decimal without pre- or post-digits")); } // toread+=postdec; // } else { // if predec==0 { return Ok(Pass); } // } // let (rd,sk,ex) = peek_tail(&bs[toread..])?; // toread+=rd; toskip=sk; exp=ex; // } // } // // self.char_buf.clear(); // for _ in 0..toread { self.char_buf.push(read(bs)? as char); } // for _ in 0..toskip { read(bs)?; } // if exp!=0 { self.char_buf.push('e'); self.char_buf.push_str(&exp.to_string()); } // // let val = self.char_buf.parse::().map_err(|_| { // KErr::new("parse error").pre(&self.char_buf) // })?; // Ok(Bite(val)) // } fn read_unaryop(&self, slab:&mut ParseSlab, bs:&mut &[u8], depth:usize) -> Result,Error> { spaces!(bs); match peek!(bs) { None => Ok(Pass), // Err(KErr::new("EOF at UnaryOp position")), -- Instead of erroring, let the higher level decide what to do. Some(b) => match b { b'+' => { skip!(bs); let v = self.read_value(slab,bs,depth+1)?; Ok(Bite(EPos(slab.push_val(v)?))) } b'-' => { skip!(bs); let v = self.read_value(slab,bs,depth+1)?; Ok(Bite(ENeg(slab.push_val(v)?))) } b'(' => { skip!(bs); let xi = self.read_expression(slab,bs,depth+1,false)?; spaces!(bs); if read!(bs,"parentheses")? != b')' { return Err(Error::Expected(")".to_string())); } Ok(Bite(EParentheses(xi))) } b'[' => { skip!(bs); let xi = self.read_expression(slab,bs,depth+1,false)?; spaces!(bs); if read!(bs,"square brackets")? != b']' { return Err(Error::Expected("]".to_string())); } Ok(Bite(EParentheses(xi))) } b'!' => { skip!(bs); let v = self.read_value(slab,bs,depth+1)?; Ok(Bite(ENot(slab.push_val(v)?))) } _ => Ok(Pass), } } } fn read_binaryop(&self, bs:&mut &[u8]) -> Result,Error> { spaces!(bs); match peek!(bs) { None => Ok(Pass), // Err(KErr::new("EOF")), -- EOF is usually OK in a BinaryOp position. Some(b) => match b { b'+' => { skip!(bs); Ok(Bite(EAdd)) } b'-' => { skip!(bs); Ok(Bite(ESub)) } b'*' => { skip!(bs); Ok(Bite(EMul)) } b'/' => { skip!(bs); Ok(Bite(EDiv)) } b'%' => { skip!(bs); Ok(Bite(EMod)) } b'^' => { skip!(bs); Ok(Bite(EExp)) } b'<' => { skip!(bs); if peek_is!(bs,0,b'=') { skip!(bs); Ok(Bite(ELTE)) } else { Ok(Bite(ELT)) } } b'>' => { skip!(bs); if peek_is!(bs,0,b'=') { skip!(bs); Ok(Bite(EGTE)) } else { Ok(Bite(EGT)) } } b'=' if peek_is!(bs,1,b'=') => { skip_n!(bs,2); Ok(Bite(EEQ)) } b'!' if peek_is!(bs,1,b'=') => { skip_n!(bs,2); Ok(Bite(ENE)) } #[cfg(feature="alpha-keywords")] b'o' if peek_is!(bs,1,b'r') => { skip_n!(bs,2); Ok(Bite(EOR)) } b'|' if peek_is!(bs,1,b'|') => { skip_n!(bs,2); Ok(Bite(EOR)) } #[cfg(feature="alpha-keywords")] b'a' if peek_is!(bs,1,b'n') && peek_is!(bs,2,b'd') => { skip_n!(bs,3); Ok(Bite(EAND)) } b'&' if peek_is!(bs,1,b'&') => { skip_n!(bs,2); Ok(Bite(EAND)) } _ => Ok(Pass), } } } fn read_callable(&self, slab:&mut ParseSlab, bs:&mut &[u8], depth:usize) -> Result,Error> { match Self::read_varname(bs)? { Pass => Ok(Pass), Bite(varname) => { match Self::read_open_parenthesis(bs)? { Pass => { // VarNames without Parenthesis are always treated as custom 0-arg functions. #[cfg(feature="unsafe-vars")] match slab.unsafe_vars.get(&varname) { None => Ok(Bite(EStdFunc(EVar(varname)))), Some(&ptr) => Ok(Bite(EStdFunc(EUnsafeVar{name:varname, ptr}))) } #[cfg(not(feature="unsafe-vars"))] Ok(Bite(EStdFunc(EVar(varname)))) } Bite(open_parenth) => { // VarNames with Parenthesis are first matched against builtins, then custom. match varname.as_ref() { "print" => Ok(Bite(EPrintFunc(self.read_printfunc(slab,bs,depth,open_parenth)?))), _ => Ok(Bite(EStdFunc(self.read_func(varname,slab,bs,depth,open_parenth)?))), } } } } } } fn read_varname(bs:&mut &[u8]) -> Result,Error> { spaces!(bs); let mut toklen = 0; while Self::is_varname_byte_opt(peek_n!(bs,toklen),toklen) { toklen=toklen+1; } if toklen==0 { return Ok(Pass); } let out = unsafe { from_utf8_unchecked(&bs[..toklen]) }.to_string(); skip_n!(bs, toklen); Ok(Bite(out)) } fn read_open_parenthesis(bs:&mut &[u8]) -> Result,Error> { spaces!(bs); match peek!(bs) { Some(b'(') | Some(b'[') => Ok(Bite(match read!(bs) { Ok(b) => b, Err(..) => return Err(Error::Unreachable), })), _ => Ok(Pass), } } fn read_func(&self, fname:String, slab:&mut ParseSlab, bs:&mut &[u8], depth:usize, open_parenth:u8) -> Result { let close_parenth = match open_parenth { b'(' => b')', b'[' => b']', _ => return Err(Error::Expected("'(' or '['".to_string())), }; let mut args = Vec::::with_capacity(4); loop { spaces!(bs); match peek!(bs) { Some(b) => { if b==close_parenth { skip!(bs); break; } } None => return Err(Error::EofWhileParsing(fname)), } if !args.is_empty() { match read!(bs) { Ok(b',') | Ok(b';') => { // I accept ',' or ';' because the TV API disallows the ',' char in symbols... so I'm using ';' as a compromise. } _ => return Err(Error::Expected("',' or ';'".to_string())), } } args.push(self.read_expression(slab,bs,depth+1,false)?); } let fname_str = fname.as_str(); match fname_str { "int" => { if args.len()==1 { Ok(EFuncInt(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("int: expected one arg".to_string())) } } "ceil" => { if args.len()==1 { Ok(EFuncCeil(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("ceil: expected one arg".to_string())) } } "floor" => { if args.len()==1 { Ok(EFuncFloor(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("floor: expected one arg".to_string())) } } "abs" => { if args.len()==1 { Ok(EFuncAbs(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("abs: expected one arg".to_string())) } } "sign" => { if args.len()==1 { Ok(EFuncSign(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("sign: expected one arg".to_string())) } } "log" => { if args.len()==1 { Ok(EFuncLog{base:None, expr:match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), }}) } else if args.len()==2 { let expr = match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), }; Ok(EFuncLog{base:Some(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), }), expr}) } else { Err(Error::WrongArgs("expected log(x) or log(base,x)".to_string())) } } "round" => { if args.len()==1 { Ok(EFuncRound{modulus:None, expr:match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), }}) } else if args.len()==2 { let expr = match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), }; Ok(EFuncRound{modulus:Some(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), }), expr}) } else { Err(Error::WrongArgs("round: expected round(x) or round(modulus,x)".to_string())) } } "min" => { if !args.is_empty() { match remove_no_panic(&mut args, 0) { Some(first) => Ok(EFuncMin{first, rest:args}), None => Err(Error::Unreachable), } } else { Err(Error::WrongArgs("min: expected one or more args".to_string())) } } "max" => { if !args.is_empty() { match remove_no_panic(&mut args, 0) { Some(first) => Ok(EFuncMax{first, rest:args}), None => Err(Error::Unreachable), } } else { Err(Error::WrongArgs("max: expected one or more args".to_string())) } } "e" => { if args.is_empty() { Ok(EFuncE) } else { Err(Error::WrongArgs("e: expected no args".to_string())) } } "pi" => { if args.is_empty() { Ok(EFuncPi) } else { Err(Error::WrongArgs("pi: expected no args".to_string())) } } "sin" => { if args.len()==1 { Ok(EFuncSin(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("sin: expected one arg".to_string())) } } "cos" => { if args.len()==1 { Ok(EFuncCos(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("cos: expected one arg".to_string())) } } "tan" => { if args.len()==1 { Ok(EFuncTan(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("tan: expected one arg".to_string())) } } "asin" => { if args.len()==1 { Ok(EFuncASin(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("asin: expected one arg".to_string())) } } "acos" => { if args.len()==1 { Ok(EFuncACos(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("acos: expected one arg".to_string())) } } "atan" => { if args.len()==1 { Ok(EFuncATan(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("atan: expected one arg".to_string())) } } "sinh" => { if args.len()==1 { Ok(EFuncSinH(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("sinh: expected one arg".to_string())) } } "cosh" => { if args.len()==1 { Ok(EFuncCosH(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("cosh: expected one arg".to_string())) } } "tanh" => { if args.len()==1 { Ok(EFuncTanH(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("tanh: expected one arg".to_string())) } } "asinh" => { if args.len()==1 { Ok(EFuncASinH(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("asinh: expected one arg".to_string())) } } "acosh" => { if args.len()==1 { Ok(EFuncACosH(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("acosh: expected one arg".to_string())) } } "atanh" => { if args.len()==1 { Ok(EFuncATanH(match args.pop() { Some(xi) => xi, None => return Err(Error::Unreachable), })) } else { Err(Error::WrongArgs("atanh: expected one arg".to_string())) } } _ => { #[cfg(feature="unsafe-vars")] match slab.unsafe_vars.get(fname_str) { None => Ok(EFunc{name:fname, args}), Some(&ptr) => Ok(EUnsafeVar{name:fname, ptr}), } #[cfg(not(feature="unsafe-vars"))] Ok(EFunc{name:fname, args}) } } } fn read_printfunc(&self, slab:&mut ParseSlab, bs:&mut &[u8], depth:usize, open_parenth:u8) -> Result { let close_parenth = match open_parenth { b'(' => b')', b'[' => b']', _ => return Err(Error::Expected("'(' or '['".to_string())), }; let mut args = Vec::::with_capacity(8); loop { spaces!(bs); match peek!(bs) { Some(b) => { if b==close_parenth { skip!(bs); break; } } None => { return Err(Error::EofWhileParsing("print".to_string())); } } if !args.is_empty() { match read!(bs) { Ok(b',') | Ok(b';') => {} _ => { return Err(Error::Expected("',' or ';'".to_string())); } } } args.push(self.read_expressionorstring(slab,bs,depth+1)?); } Ok(PrintFunc(args)) } fn read_expressionorstring(&self, slab:&mut ParseSlab, bs:&mut &[u8], depth:usize) -> Result { match Self::read_string(bs)? { Pass => {} Bite(s) => return Ok(EStr(s)), } Ok(EExpr(self.read_expression(slab,bs,depth+1,false)?)) } // TODO: Improve this logic, especially to handle embedded quotes: fn read_string(bs:&mut &[u8]) -> Result,Error> { spaces!(bs); match peek!(bs) { None => return Err(Error::EofWhileParsing("opening quote of string".to_string())), Some(b'"') => { skip!(bs); } Some(_) => { return Ok(Pass) } } let mut toklen = 0; while match peek_n!(bs,toklen) { None => false, Some(b'"') => false, Some(_) => true, } { toklen=toklen+1; } let out = from_utf8(&bs[..toklen]).map_err(|_| Error::Utf8ErrorWhileParsing("string".to_string()))?; skip_n!(bs, toklen); match read!(bs) { Err(Error::EOF) => Err(Error::EofWhileParsing("string".to_string())), Err(_) => Err(Error::Unreachable), Ok(b'"') => Ok(Bite(out.to_string())), Ok(_) => Err(Error::Unreachable), } } } impl Default for Parser { fn default() -> Self { Self::new() } } impl Default for Expression { fn default() -> Self { Expression{first:Default::default(), pairs:Vec::new()} } } impl Default for Value { fn default() -> Self { EConstant(std::f64::NAN) } } // A version of Vec::remove that doesn't panic: // (Mostly copy-pasted from https://doc.rust-lang.org/src/alloc/vec.rs.html#991-1010 .) pub(crate) fn remove_no_panic(vself:&mut Vec, index:usize) -> Option { let len = vself.len(); if index >= len { return None } unsafe { // infallible let ret; { // the place we are taking from. let ptr = vself.as_mut_ptr().add(index); // copy it out, unsafely having a copy of the value on // the stack and in the vector at the same time. ret = ptr::read(ptr); // Shift everything down to fill in that spot. ptr::copy(ptr.offset(1), ptr, len-index-1); } vself.set_len(len-1); Some(ret) } } #[cfg(test)] mod internal_tests { use super::*; use crate::slab::Slab; //// Commented so I can compile with stable Rust. // extern crate test; // use test::{Bencher, black_box}; #[test] fn rem_no_panic() { let mut v = vec![1u8, 2, 3]; assert_eq!(format!("{:?}",v), "[1, 2, 3]"); assert_eq!(remove_no_panic(&mut v,1), Some(2)); assert_eq!(remove_no_panic(&mut v,10), None); assert_eq!(format!("{:?}",v), "[1, 3]"); } #[test] fn util() { match (|| -> Result<(),Error> { let bsarr = [1,2,3]; let bs = &mut &bsarr[..]; assert_eq!(peek!(bs), Some(1)); assert_eq!(peek_n!(bs,1), Some(2)); assert_eq!(peek_n!(bs,2), Some(3)); assert_eq!(peek_n!(bs,3), None); assert_eq!(read!(bs)?, 1); skip!(bs); assert_eq!(read!(bs)?, 3); match read!(bs).err() { Some(Error::EOF) => {}, _ => panic!("I expected an EOF") } Ok(()) })() { Ok(_) => {} Err(_) => { unimplemented!(); } } assert!((&[0u8; 0]).is_empty()); assert!(!(&[1]).is_empty()); assert!((b"").is_empty()); assert!(!(b"x").is_empty()); let b=b' '; assert!(is_space!(b)); let b=b'\t'; assert!(is_space!(b)); let b=b'\r'; assert!(is_space!(b)); let b=b'\n'; assert!(is_space!(b)); let b=b'a'; assert!(!is_space!(b)); let b=b'1'; assert!(!is_space!(b)); let b=b'.'; assert!(!is_space!(b)); { let bsarr = b" abc 123 "; let bs = &mut &bsarr[..]; spaces!(bs); assert_eq!(bs, b"abc 123 "); } } #[test] fn priv_tests() { assert!(Parser::is_varname_byte_opt(Some(b'a'),0)); let mut slab = Slab::new(); { let bsarr = b"12.34"; let bs = &mut &bsarr[..]; assert_eq!(Parser::new().read_value(&mut slab.ps, bs, 0), Ok(EConstant(12.34))); } } //// Commented so I can compile this library with stable Rust. // #[bench] // #[allow(non_snake_case)] // fn spaces_1M(bencher:&mut Bencher) { // let zero = "abc".as_bytes(); // let one = " abc".as_bytes(); // let two = " abc".as_bytes(); // bencher.iter(|| { // let (z1,z2,z3,z4) = (&zero[..], &zero[..], &zero[..], &zero[..]); // Localize // let (o1,o2) = (&one[..], &one[..]); // let t1 = &two[..]; // for _ in 0..1000 { // let (z1,z2,z3,z4) = (&mut &z1[..], &mut &z2[..], &mut &z3[..], &mut &z4[..]); // let (o1,o2) = (&mut &o1[..], &mut &o2[..]); // let t1 = &mut &t1[..]; // spaces!(z1); // spaces!(z2); // spaces!(z3); // spaces!(z4); // spaces!(o1); // spaces!(o2); // spaces!(t1); // black_box(z1); // black_box(z2); // black_box(z3); // black_box(z4); // black_box(o1); // black_box(o2); // black_box(t1); // } // }); // } } fasteval-0.2.4/src/slab.rs010066400017500001750000000357411361005325600136360ustar0000000000000000//! A `Slab` is a pre-allocated block of memory, used during the //! parse/compile/eval phases to reduce memory allocation/deallocation. //! //! A `Slab` enables you to perform one single, large allocation at the //! beginning of the parse-compile-eval process, rather than many small //! allocations. You can also re-use a `Slab` for multiple expression //! parse-compile-eval cycles, greatly reducing the amount of memory //! operations. The `Slab` is the main key to `fasteval`'s excellent //! performance. //! //! You use `ExpressionI`, `ValueI`, and `InstructionI` index types to refer to //! elements within the `Slab`. These special index types are necessary to //! side-step the Rust borrow checker, which is not able to understand //! borrow-splitting of contiguous allocations (like arrays). //! (In other words, these special index types allows `fasteval` to mutate a //! `Slab` while simultaneously holding references to its contents.) //! //! You usually won't use any of the `Slab` method directly. Instead, you'll //! just pass a reference to other functions like [`parse()`](../parser/index.html), //! [`compile()`](../compiler/trait.Compiler.html) and [`eval()`](../evaler/trait.Evaler.html). //! We treat a `Slab` sort of like a Context in other programming systems. //! //! The `Slab` contains two fields: `ps` ("Parse Slab") and `cs` //! ("Compile Slab"). It is structured like this because of Rust's borrowing //! rules, so that the two fields can be borrowed and mutated independently. //! //! If you use the [`ez_eval()`](../ez/fn.ez_eval.html) function, it allocates //! a Slab for you. If you are performing the parse/compile/eval process //! yourself, then you'll need to allocate a Slab at the beginning. //! //! # Examples //! //! Here is an example of re-using one `Slab` for multiple parse/eval cycles: //! ``` //! use fasteval::Evaler; // import this trait so we can call eval(). //! fn main() -> Result<(), fasteval::Error> { //! let parser = fasteval::Parser::new(); //! let mut slab = fasteval::Slab::new(); //! //! let val = parser.parse("1+2*3-4", &mut slab.ps)?.from(&slab.ps).eval(&slab, &mut fasteval::EmptyNamespace)?; //! assert_eq!(val, 3.0); //! //! // Let's re-use the same slab again to save memory operations. //! //! // `parse()` will clear the Slab's data. It is important that you //! // do not use an old expression after the Slab has been cleared. //! let val = parser.parse("5+6*7-8", &mut slab.ps)?.from(&slab.ps).eval(&slab, &mut fasteval::EmptyNamespace)?; //! assert_eq!(val, 39.0); //! //! Ok(()) //! } //! ``` use crate::error::Error; use crate::parser::{ExpressionI, ValueI, Expression, Value}; use crate::compiler::{Instruction::{self, IConst}, InstructionI}; use std::fmt; use std::mem; #[cfg(feature="unsafe-vars")] use std::collections::BTreeMap; // Eliminate function call overhead: macro_rules! get_expr { ($pslab:expr, $i_ref:ident) => { match $pslab.exprs.get($i_ref.0) { Some(expr_ref) => expr_ref, None => &$pslab.def_expr, } }; } macro_rules! get_val { ($pslab:expr, $i_ref:ident) => { match $pslab.vals.get($i_ref.0) { Some(val_ref) => val_ref, None => &$pslab.def_val, } }; } // The CompileSlab::get_instr method is in the hot path of compiled evaluation: macro_rules! get_instr { ($cslab:expr, $i_ref:ident) => { match $cslab.instrs.get($i_ref.0) { Some(instr_ref) => instr_ref, None => &$cslab.def_instr, } }; } impl ExpressionI { /// Gets an Expression reference from the ParseSlab. /// /// This is actually just a convenience function built on top of /// `ParseSlab.get_expr`, but it enables you to perform the entire /// parse/compile/eval process in one line without upsetting the Rust /// borrow checker. (If you didn't have this function, the borrow checker /// would force you to split the process into at least two lines.) /// #[inline] pub fn from(self, ps:&ParseSlab) -> &Expression { get_expr!(ps,self) } } impl ValueI { /// Gets a Value reference from the ParseSlab. /// /// See the comments on [ExpressionI::from](struct.ExpressionI.html#method.from). /// #[inline] pub fn from(self, ps:&ParseSlab) -> &Value { get_val!(ps,self) } } /// [See the `slab module` documentation.](index.html) pub struct Slab { pub ps:ParseSlab, pub cs:CompileSlab, } /// `ParseSlab` is where `parse()` results are stored, located at `Slab.ps`. /// /// # Unsafe Variable Registration with `add_unsafe_var()` /// /// (This is documented here because the /// [`add_unsafe_var()`](#method.add_unsafe_var) method and its documentation /// only appears if `fasteval` is built with the `unsafe-vars` feature (`cargo /// build --features unsafe-vars`). I want this documentation to appear /// regardless of the build mode, so I'm putting it here.) /// /// Here is the function signature of the `add_unsafe_var()` method: /// /// ```text /// pub unsafe fn add_unsafe_var(&mut self, name: String, ptr: &f64) /// ``` /// /// If you are using [Unsafe Variables](../index.html#unsafe-variables), you /// need to pre-register the unsafe variable names and pointers *before* /// calling `parse()`. This is because Unsafe Variables are represented /// specially in the parse AST; therefore, `parse()` needs to know what /// variables are unsafe and which ones are normal so that it can produce the /// correct AST. /// /// If you forget to pre-register an unsafe variable before `parse()`, the /// variable will be treated like a Normal Variable, and you'll probably get an /// [`Undefined`](../error/enum.Error.html#variant.Undefined) error during evaluation. /// /// ## Safety /// /// You must guarantee that Unsafe Variable pointers remain valid for the /// lifetime of the resulting expression. If you continue to use an expression /// after the memory of an unsafe variable has been reclaimed, you will have /// undefined behavior. /// /// /// ## Examples /// /// Here is an example of correct and incorrect use of unsafe variable pointers: /// /// ``` /// use fasteval::Evaler; // use this trait so we can call eval(). /// use fasteval::Compiler; // use this trait so we can call compile(). /// /// // Here is an example of INCORRECT registration. DO NOT DO THIS! /// fn bad_unsafe_var(slab_mut:&mut fasteval::Slab) { /// let bad : f64 = 0.0; /// /// // Saves a pointer to 'bad': /// unsafe { slab_mut.ps.add_unsafe_var("bad".to_string(), &bad); } // `add_unsafe_var()` only exists if the `unsafe-vars` feature is enabled: `cargo test --features unsafe-vars` /// /// // 'bad' goes out-of-scope here, and the pointer we registered is no longer valid! /// // This will result in undefined behavior. /// } /// /// fn main() -> Result<(), fasteval::Error> { /// let mut slab = fasteval::Slab::new(); /// /// // The Unsafe Variable will use a pointer to read this memory location: /// // You must make sure that this variable stays in-scope as long as the /// // expression is in-use. /// let mut deg : f64 = 0.0; /// /// // Unsafe Variables must be registered before 'parse()'. /// // (Normal Variables only need definitions during the 'eval' phase.) /// unsafe { slab.ps.add_unsafe_var("deg".to_string(), °); } // `add_unsafe_var()` only exists if the `unsafe-vars` feature is enabled: `cargo test --features unsafe-vars` /// /// // bad_unsafe_var(&mut slab); // Don't do it this way. /// /// let expr_str = "sin(deg/360 * 2*pi())"; /// let expr_ref = fasteval::Parser::new().parse(expr_str, &mut slab.ps)?.from(&slab.ps); /// /// // The main reason people use Unsafe Variables is to maximize performance. /// // Compilation also helps performance, so it is usually used together with Unsafe Variables: /// let compiled = expr_ref.compile(&slab.ps, &mut slab.cs); /// /// let mut ns = fasteval::EmptyNamespace; // We only define unsafe variables, not normal variables, /// // so EmptyNamespace is fine. /// /// for d in 0..360 { /// deg = d as f64; /// let val = fasteval::eval_compiled!(compiled, &slab, &mut ns); /// eprintln!("sin({}°) = {}", deg, val); /// } /// /// Ok(()) /// } /// /// ``` pub struct ParseSlab { pub(crate) exprs :Vec, pub(crate) vals :Vec, pub(crate) def_expr :Expression, pub(crate) def_val :Value, pub(crate) char_buf :String, #[cfg(feature="unsafe-vars")] pub(crate) unsafe_vars:BTreeMap, } /// `CompileSlab` is where `compile()` results are stored, located at `Slab.cs`. pub struct CompileSlab { pub(crate) instrs :Vec, pub(crate) def_instr:Instruction, } impl ParseSlab { /// Returns a reference to the [`Expression`](../parser/struct.Expression.html) /// located at `expr_i` within the `ParseSlab.exprs'. /// /// If `expr_i` is out-of-bounds, a reference to a default `Expression` is returned. /// #[inline] pub fn get_expr(&self, expr_i:ExpressionI) -> &Expression { // I'm using this non-panic match structure to boost performance: match self.exprs.get(expr_i.0) { Some(expr_ref) => expr_ref, None => &self.def_expr, } } /// Returns a reference to the [`Value`](../parser/enum.Value.html) /// located at `val_i` within the `ParseSlab.vals'. /// /// If `val_i` is out-of-bounds, a reference to a default `Value` is returned. /// #[inline] pub fn get_val(&self, val_i:ValueI) -> &Value { match self.vals.get(val_i.0) { Some(val_ref) => val_ref, None => &self.def_val, } } /// Appends an `Expression` to `ParseSlab.exprs`. /// /// # Errors /// /// If `ParseSlab.exprs` is already full, a `SlabOverflow` error is returned. /// #[inline] pub(crate) fn push_expr(&mut self, expr:Expression) -> Result { let i = self.exprs.len(); if i>=self.exprs.capacity() { return Err(Error::SlabOverflow); } self.exprs.push(expr); Ok(ExpressionI(i)) } /// Appends a `Value` to `ParseSlab.vals`. /// /// # Errors /// /// If `ParseSlab.vals` is already full, a `SlabOverflow` error is returned. /// #[inline] pub(crate) fn push_val(&mut self, val:Value) -> Result { let i = self.vals.len(); if i>=self.vals.capacity() { return Err(Error::SlabOverflow); } self.vals.push(val); Ok(ValueI(i)) } /// Clears all data from `ParseSlab.exprs` and `ParseSlab.vals`. #[inline] pub fn clear(&mut self) { self.exprs.clear(); self.vals.clear(); } /// [See the `add_unsafe_var()` documentation above.](#unsafe-variable-registration-with-add_unsafe_var) #[cfg(feature="unsafe-vars")] #[allow(clippy::trivially_copy_pass_by_ref)] pub unsafe fn add_unsafe_var(&mut self, name:String, ptr:&f64) { self.unsafe_vars.insert(name, ptr as *const f64); } } impl CompileSlab { /// Returns a reference to the [`Instruction`](../compiler/enum.Instruction.html) /// located at `instr_i` within the `CompileSlab.instrs'. /// /// If `instr_i` is out-of-bounds, a reference to a default `Instruction` is returned. /// #[inline] pub fn get_instr(&self, instr_i:InstructionI) -> &Instruction { match self.instrs.get(instr_i.0) { Some(instr_ref) => instr_ref, None => &self.def_instr, } } /// Appends an `Instruction` to `CompileSlab.instrs`. pub(crate) fn push_instr(&mut self, instr:Instruction) -> InstructionI { if self.instrs.capacity()==0 { self.instrs.reserve(32); } let i = self.instrs.len(); self.instrs.push(instr); InstructionI(i) } /// Removes an `Instruction` from `CompileSlab.instrs` as efficiently as possible. pub(crate) fn take_instr(&mut self, i:InstructionI) -> Instruction { if i.0==self.instrs.len()-1 { match self.instrs.pop() { Some(instr) => instr, None => IConst(std::f64::NAN), } } else { match self.instrs.get_mut(i.0) { Some(instr_ref) => mem::replace(instr_ref, IConst(std::f64::NAN)), // Replace with a conspicuous value in case we use it by accident. None => IConst(std::f64::NAN), } } } /// Clears all data from `CompileSlab.instrs`. #[inline] pub fn clear(&mut self) { self.instrs.clear(); } } impl Slab { /// Creates a new default-sized `Slab`. #[inline] pub fn new() -> Self { Self::with_capacity(64) } /// Creates a new `Slab` with the given capacity. #[inline] pub fn with_capacity(cap:usize) -> Self { Self{ ps:ParseSlab{ exprs :Vec::with_capacity(cap), vals :Vec::with_capacity(cap), def_expr :Default::default(), def_val :Default::default(), char_buf :String::with_capacity(64), #[cfg(feature="unsafe-vars")] unsafe_vars:BTreeMap::new(), }, cs:CompileSlab{ instrs :Vec::new(), // Don't pre-allocate for compilation. def_instr:Default::default(), }, } } /// Clears all data from [`Slab.ps`](struct.ParseSlab.html) and [`Slab.cs`](struct.CompileSlab.html). #[inline] pub fn clear(&mut self) { self.ps.exprs.clear(); self.ps.vals.clear(); self.cs.instrs.clear(); } } fn write_indexed_list(f:&mut fmt::Formatter, lst:&[T]) -> Result<(), fmt::Error> where T:fmt::Debug { write!(f, "{{")?; let mut nonempty = false; for (i,x) in lst.iter().enumerate() { if nonempty { write!(f, ",")?; } nonempty = true; write!(f, " {}:{:?}",i,x)?; } if nonempty { write!(f, " ")?; } write!(f, "}}")?; Ok(()) } impl fmt::Debug for Slab { fn fmt(&self, f:&mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "Slab{{ exprs:")?; write_indexed_list(f, &self.ps.exprs)?; write!(f, ", vals:")?; write_indexed_list(f, &self.ps.vals)?; write!(f, ", instrs:")?; write_indexed_list(f, &self.cs.instrs)?; write!(f, " }}")?; Ok(()) } } impl fmt::Debug for ParseSlab { fn fmt(&self, f:&mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "ParseSlab{{ exprs:")?; write_indexed_list(f, &self.exprs)?; write!(f, ", vals:")?; write_indexed_list(f, &self.vals)?; write!(f, " }}")?; Ok(()) } } impl fmt::Debug for CompileSlab { fn fmt(&self, f:&mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "CompileSlab{{ instrs:")?; write_indexed_list(f, &self.instrs)?; write!(f, " }}")?; Ok(()) } } impl Default for Slab { fn default() -> Self { Self::with_capacity(64) } } fasteval-0.2.4/tests/compile.rs010066400017500001750000001063251361005202300147040ustar0000000000000000use fasteval::{Parser, Compiler, Evaler, Error, Slab, EmptyNamespace, CachedCallbackNamespace, ExpressionI, InstructionI, eval_compiled, eval_compiled_ref}; use fasteval::parser::{PrintFunc, ExpressionOrString::{EExpr, EStr}}; #[cfg(feature="eval-builtin")] use fasteval::parser::{EvalFunc, KWArg}; use fasteval::compiler::IC; use fasteval::compiler::Instruction::{self, IConst, INeg, INot, IInv, IAdd, IMul, IMod, IExp, ILT, ILTE, IEQ, INE, IGTE, IGT, IAND, IOR, IVar, IFunc, IFuncInt, IFuncCeil, IFuncFloor, IFuncAbs, IFuncSign, IFuncLog, IFuncRound, IFuncMin, IFuncMax, IFuncSin, IFuncCos, IFuncTan, IFuncASin, IFuncACos, IFuncATan, IFuncSinH, IFuncCosH, IFuncTanH, IFuncASinH, IFuncACosH, IFuncATanH, IPrintFunc}; #[cfg(feature="eval-builtin")] use fasteval::compiler::Instruction::IEvalFunc; #[test] fn slab_overflow() { let mut slab = Slab::with_capacity(2); assert_eq!(Parser::new().parse("1 + 2 + -3 + ( +4 )", &mut slab.ps), Ok(ExpressionI(1))); assert_eq!(format!("{:?}", slab), "Slab{ exprs:{ 0:Expression { first: EConstant(4.0), pairs: [] }, 1:Expression { first: EConstant(1.0), pairs: [ExprPair(EAdd, EConstant(2.0)), ExprPair(EAdd, EConstant(-3.0)), ExprPair(EAdd, EUnaryOp(EParentheses(ExpressionI(0))))] } }, vals:{}, instrs:{} }"); assert_eq!(Parser::new().parse("1 + 2 + -3 + ( ++4 )", &mut slab.ps), Ok(ExpressionI(1))); assert_eq!(format!("{:?}", slab), "Slab{ exprs:{ 0:Expression { first: EUnaryOp(EPos(ValueI(0))), pairs: [] }, 1:Expression { first: EConstant(1.0), pairs: [ExprPair(EAdd, EConstant(2.0)), ExprPair(EAdd, EConstant(-3.0)), ExprPair(EAdd, EUnaryOp(EParentheses(ExpressionI(0))))] } }, vals:{ 0:EConstant(4.0) }, instrs:{} }"); assert_eq!(Parser::new().parse("1 + 2 + -3 + ( +++4 )", &mut slab.ps), Ok(ExpressionI(1))); assert_eq!(format!("{:?}", slab), "Slab{ exprs:{ 0:Expression { first: EUnaryOp(EPos(ValueI(1))), pairs: [] }, 1:Expression { first: EConstant(1.0), pairs: [ExprPair(EAdd, EConstant(2.0)), ExprPair(EAdd, EConstant(-3.0)), ExprPair(EAdd, EUnaryOp(EParentheses(ExpressionI(0))))] } }, vals:{ 0:EConstant(4.0), 1:EUnaryOp(EPos(ValueI(0))) }, instrs:{} }"); assert_eq!(Parser::new().parse("1 + 2 + -3 + ( ++++4 )", &mut slab.ps), Err(Error::SlabOverflow)); } #[test] fn basics() { let mut slab = Slab::new(); let mut ns = EmptyNamespace; let expr_i = Parser::new().parse("3*3-3/3+1", &mut slab.ps).unwrap(); let expr_ref = slab.ps.get_expr(expr_i); let instr = expr_ref.compile(&slab.ps, &mut slab.cs); assert_eq!(instr, IConst(9.0)); assert_eq!(format!("{:?}", slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EMul, EConstant(3.0)), ExprPair(ESub, EConstant(3.0)), ExprPair(EDiv, EConstant(3.0)), ExprPair(EAdd, EConstant(1.0))] } }, vals:{}, instrs:{} }"); (|| -> Result<(),Error> { assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), 9.0); assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), 9.0); Ok(()) })().unwrap(); } fn comp(expr_str:&str) -> (Slab, Instruction) { let mut slab = Slab::new(); let instr = Parser::new().parse(expr_str, &mut slab.ps).unwrap().from(&slab.ps).compile(&slab.ps, &mut slab.cs); (slab, instr) } fn comp_chk(expr_str:&str, expect_instr:Instruction, expect_fmt:&str, expect_eval:f64) { let mut slab = Slab::new(); let expr = Parser::new().parse(expr_str, &mut slab.ps).unwrap().from(&slab.ps); let instr = expr.compile(&slab.ps, &mut slab.cs); assert_eq!(instr, expect_instr); assert_eq!(format!("{:?}",slab.cs), expect_fmt); let mut ns = CachedCallbackNamespace::new(|name,args| { match name { "w" => Some(0.0), "x" => Some(1.0), "y" => Some(2.0), "y7" => Some(2.7), "z" => Some(3.0), "foo" => Some(args[0]*10.0), "bar" => Some(args[0]+args[1]), _ => None, } }); (|| -> Result<(),Error> { assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), expect_eval); // Make sure Instruction eval matches normal eval: assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), expr.eval(&slab, &mut ns).unwrap()); Ok(()) })().unwrap(); } #[cfg(feature="unsafe-vars")] fn unsafe_comp_chk(expr_str:&str, expect_fmt:&str, expect_eval:f64) { fn replace_addrs(mut s:String) -> String { let mut start=0; loop { match s[start..].find(" 0x") { None => break, Some(i) => { let v = unsafe { s.as_mut_vec() }; start = start+i+3; loop { match v.get(start) { None => break, Some(&b) => { if (b'0'<=b && b<=b'9') || (b'a'<=b && b<=b'f') { v[start]=b'?'; start+=1; } else { break; } } } } } }; } s } let mut slab = Slab::new(); let w = 0.0; let x = 1.0; let y = 2.0; let y7 = 2.7; let z = 3.0; unsafe { slab.ps.add_unsafe_var("w".to_string(), &w); slab.ps.add_unsafe_var("x".to_string(), &x); slab.ps.add_unsafe_var("y".to_string(), &y); slab.ps.add_unsafe_var("y7".to_string(), &y7); slab.ps.add_unsafe_var("z".to_string(), &z); } let expr = Parser::new().parse(expr_str, &mut slab.ps).unwrap().from(&slab.ps); let instr = expr.compile(&slab.ps, &mut slab.cs); assert_eq!(replace_addrs(format!("{:?}",slab.cs)), expect_fmt); (|| -> Result<(),Error> { let mut ns = EmptyNamespace; assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), expect_eval); // Make sure Instruction eval matches normal eval: assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), expr.eval(&slab, &mut ns).unwrap()); Ok(()) })().unwrap(); } fn comp_chk_str(expr_str:&str, expect_instr:&str, expect_fmt:&str, expect_eval:f64) { let mut slab = Slab::new(); let expr = Parser::new().parse(expr_str, &mut slab.ps).unwrap().from(&slab.ps); let instr = expr.compile(&slab.ps, &mut slab.cs); assert_eq!(format!("{:?}",instr), expect_instr); assert_eq!(format!("{:?}",slab.cs), expect_fmt); let mut ns = CachedCallbackNamespace::new(|name,args| { match name { "w" => Some(0.0), "x" => Some(1.0), "y" => Some(2.0), "y7" => Some(2.7), "z" => Some(3.0), "foo" => Some(args[0]*10.0), "bar" => Some(args[0]+args[1]), _ => None, } }); (|| -> Result<(),Error> { if expect_eval.is_nan() { assert!(eval_compiled_ref!(&instr, &slab, &mut ns).is_nan()); assert!(eval_compiled_ref!(&instr, &slab, &mut ns).is_nan()); assert!(expr.eval(&slab, &mut ns).unwrap().is_nan()); } else { assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), expect_eval); // Make sure Instruction eval matches normal eval: assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), expr.eval(&slab, &mut ns).unwrap()); } Ok(()) })().unwrap(); } #[test] fn double_neg() { assert_eq!(comp("1+1.5").1, IConst(2.5)); assert_eq!(comp("-1.5").1, IConst(-1.5)); assert_eq!(comp("--1.5").1, IConst(1.5)); assert_eq!(comp("1 + -1.5").1, IConst(-0.5)); assert_eq!(comp("1 + --1.5").1, IConst(2.5)); assert_eq!(comp("1 + ----1.5").1, IConst(2.5)); assert_eq!(comp("1 - ----1.5").1, IConst(-0.5)); assert_eq!(comp("x").1, IVar("x".to_string())); comp_chk("1-1", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("1 + x", IAdd(InstructionI(0), IC::C(1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 2.0); comp_chk("x + 1", IAdd(InstructionI(0), IC::C(1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 2.0); comp_chk("0.5 + x + 0.5", IAdd(InstructionI(0), IC::C(1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 2.0); comp_chk("0.5 - x - 0.5", INeg(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", -1.0); comp_chk("0.5 - -x - 0.5", IVar("x".to_string()), "CompileSlab{ instrs:{} }", 1.0); comp_chk("0.5 - --x - 1.5", IAdd(InstructionI(1), IC::C(-1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\"), 1:INeg(InstructionI(0)) } }", -2.0); comp_chk("0.5 - ---x - 1.5", IAdd(InstructionI(0), IC::C(-1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 0.0); comp_chk("0.5 - (---x) - 1.5", IAdd(InstructionI(0), IC::C(-1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 0.0); comp_chk("0.5 - -(--x) - 1.5", IAdd(InstructionI(0), IC::C(-1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 0.0); comp_chk("0.5 - --(-x) - 1.5", IAdd(InstructionI(0), IC::C(-1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 0.0); comp_chk("0.5 - --(-x - 1.5)", IAdd(InstructionI(3), IC::C(0.5)), "CompileSlab{ instrs:{ 0:IVar(\"x\"), 1:INeg(InstructionI(0)), 2:IAdd(InstructionI(1), C(-1.5)), 3:INeg(InstructionI(2)) } }", 3.0); comp_chk("0.5 - --((((-(x)) - 1.5)))", IAdd(InstructionI(3), IC::C(0.5)), "CompileSlab{ instrs:{ 0:IVar(\"x\"), 1:INeg(InstructionI(0)), 2:IAdd(InstructionI(1), C(-1.5)), 3:INeg(InstructionI(2)) } }", 3.0); comp_chk("0.5 - -(-(--((((-(x)) - 1.5)))))", IAdd(InstructionI(3), IC::C(0.5)), "CompileSlab{ instrs:{ 0:IVar(\"x\"), 1:INeg(InstructionI(0)), 2:IAdd(InstructionI(1), C(-1.5)), 3:INeg(InstructionI(2)) } }", 3.0); } #[test] fn all_instrs() { // IConst: comp_chk("1", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("-1", IConst(-1.0), "CompileSlab{ instrs:{} }", -1.0); // IVar: comp_chk("x", IVar("x".to_string()), "CompileSlab{ instrs:{} }", 1.0); comp_chk("x()", IFunc { name:"x".to_string(), args:vec![] }, "CompileSlab{ instrs:{} }", 1.0); comp_chk("x[]", IFunc { name:"x".to_string(), args:vec![] }, "CompileSlab{ instrs:{} }", 1.0); // INeg: comp_chk("-x", INeg(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", -1.0); // INot: comp_chk("!x", INot(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 0.0); // IInv: comp_chk("1/x", IInv(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 1.0); // IAdd: comp_chk("1 + x", IAdd(InstructionI(0), IC::C(1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 2.0); comp_chk("1 - x", IAdd(InstructionI(1), IC::C(1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\"), 1:INeg(InstructionI(0)) } }", 0.0); comp_chk("x + 2+pi()-360", IAdd(InstructionI(0), IC::C(-354.8584073464102)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", -353.8584073464102); comp_chk("x-360 + 2+pi()", IAdd(InstructionI(0), IC::C(-354.8584073464102)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", -353.8584073464102); comp_chk("1 - -(x-360 + 2+pi())", IAdd(InstructionI(1), IC::C(1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\"), 1:IAdd(InstructionI(0), C(-354.8584073464102)) } }", -352.8584073464102); comp_chk("3 + 3 - 3 + 3 - 3 + 3", IConst(6.0), "CompileSlab{ instrs:{} }", 6.0); comp_chk("3 + x - 3 + 3 + y - 3", IAdd(InstructionI(0), IC::I(InstructionI(1))), "CompileSlab{ instrs:{ 0:IVar(\"x\"), 1:IVar(\"y\") } }", 3.0); // IMul: comp_chk("2 * x", IMul(InstructionI(0), IC::C(2.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 2.0); comp_chk("x * 2", IMul(InstructionI(0), IC::C(2.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 2.0); comp_chk("x / 2", IMul(InstructionI(0), IC::C(0.5)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 0.5); comp_chk("x * 2*pi()/360", IMul(InstructionI(0), IC::C(0.017453292519943295)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 0.017453292519943295); comp_chk("x/360 * 2*pi()", IMul(InstructionI(0), IC::C(0.017453292519943295)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 0.017453292519943295); comp_chk("1 / -(x/360 * 2*pi())", IInv(InstructionI(2)), "CompileSlab{ instrs:{ 0:IVar(\"x\"), 1:IMul(InstructionI(0), C(0.017453292519943295)), 2:INeg(InstructionI(1)) } }", -57.29577951308232); comp_chk("3 * 3 / 3 * 3 / 3 * 3", IConst(9.0), "CompileSlab{ instrs:{} }", 9.0); comp_chk("3 * x / 3 * 3 * y / 3", IMul(InstructionI(0), IC::I(InstructionI(1))), "CompileSlab{ instrs:{ 0:IVar(\"x\"), 1:IVar(\"y\") } }", 2.0); // IMod: comp_chk("8 % 3", IConst(2.0), "CompileSlab{ instrs:{} }", 2.0); comp_chk("8 % z", IMod { dividend: IC::C(8.0), divisor: IC::I(InstructionI(0)) }, "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 2.0); comp_chk("-8 % 3", IConst(-2.0), "CompileSlab{ instrs:{} }", -2.0); comp_chk("8 % -3", IConst(2.0), "CompileSlab{ instrs:{} }", 2.0); comp_chk("-8 % z", IMod { dividend: IC::C(-8.0), divisor: IC::I(InstructionI(0)) }, "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", -2.0); comp_chk("8 % -z", IMod { dividend: IC::C(8.0), divisor: IC::I(InstructionI(1)) }, "CompileSlab{ instrs:{ 0:IVar(\"z\"), 1:INeg(InstructionI(0)) } }", 2.0); comp_chk("8 % 3 % 2", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("8 % z % 2", IMod { dividend: IC::I(InstructionI(1)), divisor: IC::C(2.0) }, "CompileSlab{ instrs:{ 0:IVar(\"z\"), 1:IMod { dividend: C(8.0), divisor: I(InstructionI(0)) } } }", 0.0); // IExp: comp_chk("2 ^ 3", IConst(8.0), "CompileSlab{ instrs:{} }", 8.0); comp_chk("2 ^ z", IExp { base: IC::C(2.0), power: IC::I(InstructionI(0)) }, "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 8.0); comp_chk("4 ^ 0.5", IConst(2.0), "CompileSlab{ instrs:{} }", 2.0); comp_chk("2 ^ 0.5", IConst(1.4142135623730951), "CompileSlab{ instrs:{} }", 1.4142135623730951); comp_chk_str("-4 ^ 0.5", "IConst(NaN)", "CompileSlab{ instrs:{} }", std::f64::NAN); comp_chk("y ^ 0.5", IExp { base: IC::I(InstructionI(0)), power: IC::C(0.5) }, "CompileSlab{ instrs:{ 0:IVar(\"y\") } }", 1.4142135623730951); comp_chk("2 ^ 3 ^ 2", IConst(512.0), "CompileSlab{ instrs:{} }", 512.0); comp_chk("2 ^ z ^ 2", IExp { base: IC::C(2.0), power: IC::I(InstructionI(1)) }, "CompileSlab{ instrs:{ 0:IVar(\"z\"), 1:IExp { base: I(InstructionI(0)), power: C(2.0) } } }", 512.0); comp_chk("2 ^ z ^ 1 ^ 2 ^ 1", IExp { base: IC::C(2.0), power: IC::I(InstructionI(1)) }, "CompileSlab{ instrs:{ 0:IVar(\"z\"), 1:IExp { base: I(InstructionI(0)), power: C(1.0) } } }", 8.0); // ILT: comp_chk("2 < 3", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("2 < z", ILT(IC::C(2.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3 < 3", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("3 < z", ILT(IC::C(3.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("1 < 2 < 3", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); // ILTE: comp_chk("2 <= 3", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("2 <= z", ILTE(IC::C(2.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3 <= 3", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("3 <= z", ILTE(IC::C(3.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("4 <= 3", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("4 <= z", ILTE(IC::C(4.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); // IEQ: comp_chk("2 == 3", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("2 == z", IEQ(IC::C(2.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3 == 3", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("3 == z", IEQ(IC::C(3.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("4 == 3", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("4 == z", IEQ(IC::C(4.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("4 == z == 1.0", IEQ(IC::I(InstructionI(1)), IC::C(1.0)), "CompileSlab{ instrs:{ 0:IVar(\"z\"), 1:IEQ(C(4.0), I(InstructionI(0))) } }", 0.0); comp_chk("3.1 == z", IEQ(IC::C(3.1), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.01 == z", IEQ(IC::C(3.01), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.001 == z", IEQ(IC::C(3.001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.0001 == z", IEQ(IC::C(3.0001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.00001 == z", IEQ(IC::C(3.00001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.000001 == z", IEQ(IC::C(3.000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.0000001 == z", IEQ(IC::C(3.0000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.00000001 == z", IEQ(IC::C(3.00000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.000000001 == z", IEQ(IC::C(3.000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.0000000001 == z", IEQ(IC::C(3.0000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.00000000001 == z", IEQ(IC::C(3.00000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.000000000001 == z", IEQ(IC::C(3.000000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.0000000000001 == z", IEQ(IC::C(3.0000000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.00000000000001 == z", IEQ(IC::C(3.00000000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.000000000000001 == z", IEQ(IC::C(3.000000000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.0000000000000001 == z", IEQ(IC::C(3.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); // INE: comp_chk("2 != 3", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("2 != z", INE(IC::C(2.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3 != 3", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("3 != z", INE(IC::C(3.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("4 != 3", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("4 != z", INE(IC::C(4.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.1 != z", INE(IC::C(3.1), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.01 != z", INE(IC::C(3.01), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.001 != z", INE(IC::C(3.001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.0001 != z", INE(IC::C(3.0001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.00001 != z", INE(IC::C(3.00001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.000001 != z", INE(IC::C(3.000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.0000001 != z", INE(IC::C(3.0000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.00000001 != z", INE(IC::C(3.00000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.000000001 != z", INE(IC::C(3.000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.0000000001 != z", INE(IC::C(3.0000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.00000000001 != z", INE(IC::C(3.00000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.000000000001 != z", INE(IC::C(3.000000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.0000000000001 != z", INE(IC::C(3.0000000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.00000000000001 != z", INE(IC::C(3.00000000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3.000000000000001 != z", INE(IC::C(3.000000000000001), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3.0000000000000001 != z", INE(IC::C(3.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); // IGTE: comp_chk("2 >= 3", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("2 >= z", IGTE(IC::C(2.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3 >= 3", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("3 >= z", IGTE(IC::C(3.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("4 >= 3", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("4 >= z", IGTE(IC::C(4.0), IC::I(InstructionI(0))), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); // IGT: comp_chk("3 > 2", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("z > 2", IGT(IC::I(InstructionI(0)), IC::C(2.0)), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 1.0); comp_chk("3 > 3", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("z > 3", IGT(IC::I(InstructionI(0)), IC::C(3.0)), "CompileSlab{ instrs:{ 0:IVar(\"z\") } }", 0.0); comp_chk("3 > 2 > 1", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); // IAND: comp_chk("2 and 3", IConst(3.0), "CompileSlab{ instrs:{} }", 3.0); comp_chk("2 && 3", IConst(3.0), "CompileSlab{ instrs:{} }", 3.0); comp_chk("2 and 3 and 4", IConst(4.0), "CompileSlab{ instrs:{} }", 4.0); comp_chk("2 && 3 && 4", IConst(4.0), "CompileSlab{ instrs:{} }", 4.0); comp_chk("0 and 1 and 2", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("0 && 1 && 2", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("1 and 0 and 2", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("1 && 0 && 2", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("1 and 2 and 0", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("1 && 2 && 0", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("x and 2", IAND(InstructionI(0), IC::C(2.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 2.0); comp_chk("0 and x", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("w and x", IAND(InstructionI(0), IC::I(InstructionI(1))), "CompileSlab{ instrs:{ 0:IVar(\"w\"), 1:IVar(\"x\") } }", 0.0); // IOR: comp_chk("2 or 3", IConst(2.0), "CompileSlab{ instrs:{} }", 2.0); comp_chk("2 || 3", IConst(2.0), "CompileSlab{ instrs:{} }", 2.0); comp_chk("2 or 3 or 4", IConst(2.0), "CompileSlab{ instrs:{} }", 2.0); comp_chk("2 || 3 || 4", IConst(2.0), "CompileSlab{ instrs:{} }", 2.0); comp_chk("0 or 1 or 2", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("0 || 1 || 2", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("1 or 0 or 2", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("1 || 0 || 2", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("1 or 2 or 0", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("1 || 2 || 0", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("x or 2", IOR(InstructionI(0), IC::C(2.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 1.0); comp_chk("0 or x", IVar("x".to_string()), "CompileSlab{ instrs:{} }", 1.0); comp_chk("w or x", IOR(InstructionI(0), IC::I(InstructionI(1))), "CompileSlab{ instrs:{ 0:IVar(\"w\"), 1:IVar(\"x\") } }", 1.0); comp_chk("x or w", IOR(InstructionI(0), IC::I(InstructionI(1))), "CompileSlab{ instrs:{ 0:IVar(\"x\"), 1:IVar(\"w\") } }", 1.0); // IVar comp_chk("x", IVar("x".to_string()), "CompileSlab{ instrs:{} }", 1.0); { let (_s,i) = comp("int"); assert_eq!(i, IVar("int".to_string())); let (_s,i) = comp("print"); assert_eq!(i, IVar("print".to_string())); let (_s,i) = comp("eval"); assert_eq!(i, IVar("eval".to_string())); } // IUnsafeVar #[cfg(feature="unsafe-vars")] { unsafe_comp_chk("x", "CompileSlab{ instrs:{} }", 1.0); unsafe_comp_chk("x + y", "CompileSlab{ instrs:{ 0:IUnsafeVar { name: \"x\", ptr: 0x???????????? }, 1:IUnsafeVar { name: \"y\", ptr: 0x???????????? } } }", 3.0); unsafe_comp_chk("x() + y", "CompileSlab{ instrs:{ 0:IUnsafeVar { name: \"x\", ptr: 0x???????????? }, 1:IUnsafeVar { name: \"y\", ptr: 0x???????????? } } }", 3.0); unsafe_comp_chk("x(x,y,z) + y", "CompileSlab{ instrs:{ 0:IUnsafeVar { name: \"x\", ptr: 0x???????????? }, 1:IUnsafeVar { name: \"y\", ptr: 0x???????????? } } }", 3.0); } // IFunc comp_chk("foo(2.7)", IFunc { name: "foo".to_string(), args:vec![IC::C(2.7)] }, "CompileSlab{ instrs:{} }", 27.0); comp_chk("foo(2.7, 3.4)", IFunc { name: "foo".to_string(), args:vec![IC::C(2.7), IC::C(3.4)] }, "CompileSlab{ instrs:{} }", 27.0); // IFuncInt comp_chk("int(2.7)", IConst(2.0), "CompileSlab{ instrs:{} }", 2.0); comp_chk("int(y7)", IFuncInt(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 2.0); comp_chk("int(-2.7)", IConst(-2.0), "CompileSlab{ instrs:{} }", -2.0); comp_chk("int(-y7)", IFuncInt(InstructionI(1)), "CompileSlab{ instrs:{ 0:IVar(\"y7\"), 1:INeg(InstructionI(0)) } }", -2.0); // IFuncCeil comp_chk("ceil(2.7)", IConst(3.0), "CompileSlab{ instrs:{} }", 3.0); comp_chk("ceil(y7)", IFuncCeil(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 3.0); comp_chk("ceil(-2.7)", IConst(-2.0), "CompileSlab{ instrs:{} }", -2.0); comp_chk("ceil(-y7)", IFuncCeil(InstructionI(1)), "CompileSlab{ instrs:{ 0:IVar(\"y7\"), 1:INeg(InstructionI(0)) } }", -2.0); // IFuncFloor comp_chk("floor(2.7)", IConst(2.0), "CompileSlab{ instrs:{} }", 2.0); comp_chk("floor(y7)", IFuncFloor(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 2.0); comp_chk("floor(-2.7)", IConst(-3.0), "CompileSlab{ instrs:{} }", -3.0); comp_chk("floor(-y7)", IFuncFloor(InstructionI(1)), "CompileSlab{ instrs:{ 0:IVar(\"y7\"), 1:INeg(InstructionI(0)) } }", -3.0); // IFuncAbs comp_chk("abs(2.7)", IConst(2.7), "CompileSlab{ instrs:{} }", 2.7); comp_chk("abs(y7)", IFuncAbs(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 2.7); comp_chk("abs(-2.7)", IConst(2.7), "CompileSlab{ instrs:{} }", 2.7); comp_chk("abs(-y7)", IFuncAbs(InstructionI(1)), "CompileSlab{ instrs:{ 0:IVar(\"y7\"), 1:INeg(InstructionI(0)) } }", 2.7); // IFuncSign comp_chk("sign(2.7)", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("sign(y7)", IFuncSign(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 1.0); comp_chk("sign(-2.7)", IConst(-1.0), "CompileSlab{ instrs:{} }", -1.0); comp_chk("sign(-y7)", IFuncSign(InstructionI(1)), "CompileSlab{ instrs:{ 0:IVar(\"y7\"), 1:INeg(InstructionI(0)) } }", -1.0); // IFuncLog comp_chk("log(1)", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("log(10)", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("log(2, 10)", IConst(3.321928094887362), "CompileSlab{ instrs:{} }", 3.321928094887362); comp_chk("log(e(), 10)", IConst(2.302585092994046), "CompileSlab{ instrs:{} }", 2.302585092994046); comp_chk("log(x)", IFuncLog { base: IC::C(10.0), of: IC::I(InstructionI(0)) }, "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 0.0); comp_chk("log(y,x)", IFuncLog { base: IC::I(InstructionI(0)), of: IC::I(InstructionI(1)) }, "CompileSlab{ instrs:{ 0:IVar(\"y\"), 1:IVar(\"x\") } }", 0.0); // IFuncRound comp_chk("round(2.7)", IConst(3.0), "CompileSlab{ instrs:{} }", 3.0); comp_chk("round(-2.7)", IConst(-3.0), "CompileSlab{ instrs:{} }", -3.0); comp_chk("round(y7)", IFuncRound { modulus: IC::C(1.0), of: IC::I(InstructionI(0)) }, "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 3.0); // IFuncMin comp_chk("min(2.7)", IConst(2.7), "CompileSlab{ instrs:{} }", 2.7); comp_chk("min(2.7, 3.7)", IConst(2.7), "CompileSlab{ instrs:{} }", 2.7); comp_chk("min(4.7, 3.7, 2.7)", IConst(2.7), "CompileSlab{ instrs:{} }", 2.7); comp_chk("min(y7)", IVar("y7".to_string()), "CompileSlab{ instrs:{} }", 2.7); comp_chk("min(4.7, y7, 3.7)", IFuncMin(InstructionI(0), IC::C(3.7)), "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 2.7); comp_chk("min(3.7, y7, 4.7)", IFuncMin(InstructionI(0), IC::C(3.7)), "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 2.7); comp_chk_str("min(NaN, y7, 4.7)", "IFuncMin(InstructionI(0), C(NaN))", "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", std::f64::NAN); comp_chk_str("min(NaN, 4.7)", "IConst(NaN)", "CompileSlab{ instrs:{} }", std::f64::NAN); comp_chk_str("min(inf, y7, 4.7)", "IFuncMin(InstructionI(0), C(4.7))", "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 2.7); comp_chk_str("min(inf, 4.7)", "IConst(4.7)", "CompileSlab{ instrs:{} }", 4.7); comp_chk_str("min(-inf, y7, 4.7)", "IFuncMin(InstructionI(0), C(-inf))", "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", std::f64::NEG_INFINITY); comp_chk_str("min(-inf, 4.7)", "IConst(-inf)", "CompileSlab{ instrs:{} }", std::f64::NEG_INFINITY); // IFuncMax comp_chk("max(2.7)", IConst(2.7), "CompileSlab{ instrs:{} }", 2.7); comp_chk("max(2.7, 1.7)", IConst(2.7), "CompileSlab{ instrs:{} }", 2.7); comp_chk("max(0.7, 1.7, 2.7)", IConst(2.7), "CompileSlab{ instrs:{} }", 2.7); comp_chk("max(y7)", IVar("y7".to_string()), "CompileSlab{ instrs:{} }", 2.7); comp_chk("max(0.7, y7, 1.7)", IFuncMax(InstructionI(0), IC::C(1.7)), "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 2.7); comp_chk("max(1.7, y7, 0.7)", IFuncMax(InstructionI(0), IC::C(1.7)), "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 2.7); comp_chk_str("max(NaN, y7, 0.7)", "IFuncMax(InstructionI(0), C(NaN))", "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", std::f64::NAN); comp_chk_str("max(NaN, 0.7)", "IConst(NaN)", "CompileSlab{ instrs:{} }", std::f64::NAN); comp_chk_str("max(inf, y7, 4.7)", "IFuncMax(InstructionI(0), C(inf))", "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", std::f64::INFINITY); comp_chk_str("max(inf, 4.7)", "IConst(inf)", "CompileSlab{ instrs:{} }", std::f64::INFINITY); comp_chk_str("max(-inf, y7, 4.7)", "IFuncMax(InstructionI(0), C(4.7))", "CompileSlab{ instrs:{ 0:IVar(\"y7\") } }", 4.7); comp_chk_str("max(-inf, 4.7)", "IConst(4.7)", "CompileSlab{ instrs:{} }", 4.7); // IFuncSin comp_chk("sin(0)", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("round(0.000001, sin(pi()))", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("sin(pi()/2)", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("sin(w)", IFuncSin(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", 0.0); comp_chk("sin(pi()/y)", IFuncSin(InstructionI(2)), "CompileSlab{ instrs:{ 0:IVar(\"y\"), 1:IInv(InstructionI(0)), 2:IMul(InstructionI(1), C(3.141592653589793)) } }", 1.0); // IFuncCos comp_chk("cos(0)", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("cos(pi())", IConst(-1.0), "CompileSlab{ instrs:{} }", -1.0); comp_chk("round(0.000001, cos(pi()/2))", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("cos(w)", IFuncCos(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", 1.0); comp_chk("round(0.000001, cos(pi()/y))", IFuncRound { modulus: IC::C(0.000001,), of: IC::I(InstructionI(3)) }, "CompileSlab{ instrs:{ 0:IVar(\"y\"), 1:IInv(InstructionI(0)), 2:IMul(InstructionI(1), C(3.141592653589793)), 3:IFuncCos(InstructionI(2)) } }", 0.0); // IFuncTan comp_chk("tan(0)", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("tan(w)", IFuncTan(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", 0.0); // IFuncASin comp_chk("asin(0)", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("asin(w)", IFuncASin(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", 0.0); // IFuncACos comp_chk("acos(0)", IConst(1.5707963267948966), "CompileSlab{ instrs:{} }", 1.5707963267948966); comp_chk("acos(w)", IFuncACos(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", 1.5707963267948966); // IFuncATan comp_chk("atan(0)", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("atan(w)", IFuncATan(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", 0.0); // IFuncSinH comp_chk("sinh(0)", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("sinh(w)", IFuncSinH(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", 0.0); // IFuncCosH comp_chk("cosh(0)", IConst(1.0), "CompileSlab{ instrs:{} }", 1.0); comp_chk("cosh(w)", IFuncCosH(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", 1.0); // IFuncTanH comp_chk("tanh(0)", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("tanh(w)", IFuncTanH(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", 0.0); // IFuncASinH comp_chk("asinh(0)", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("asinh(w)", IFuncASinH(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", 0.0); // IFuncACosH comp_chk("acosh(1)", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("acosh(x)", IFuncACosH(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 0.0); // IFuncATanH comp_chk("atanh(0)", IConst(0.0), "CompileSlab{ instrs:{} }", 0.0); comp_chk("atanh(w)", IFuncATanH(InstructionI(0)), "CompileSlab{ instrs:{ 0:IVar(\"w\") } }", 0.0); // IPrintFunc comp_chk(r#"print("test",1.23)"#, IPrintFunc(PrintFunc(vec![EStr("test".to_string()), EExpr(ExpressionI(0))])), "CompileSlab{ instrs:{} }", 1.23); } #[test] fn custom_func() { comp_chk("x + 1", IAdd(InstructionI(0), IC::C(1.0)), "CompileSlab{ instrs:{ 0:IVar(\"x\") } }", 2.0); comp_chk("x() + 1", IAdd(InstructionI(0), IC::C(1.0)), "CompileSlab{ instrs:{ 0:IFunc { name: \"x\", args: [] } } }", 2.0); comp_chk("x(1,2,3) + 1", IAdd(InstructionI(0), IC::C(1.0)), "CompileSlab{ instrs:{ 0:IFunc { name: \"x\", args: [C(1.0), C(2.0), C(3.0)] } } }", 2.0); comp_chk("x(1, 1+1, 1+1+1) + 1", IAdd(InstructionI(0), IC::C(1.0)), "CompileSlab{ instrs:{ 0:IFunc { name: \"x\", args: [C(1.0), C(2.0), C(3.0)] } } }", 2.0); } #[test] fn eval_macro() { fn wrapped() -> Result<(),Error> { let mut ns = EmptyNamespace; let mut slab = Slab::new(); let expr = Parser::new().parse("5", &mut slab.ps).unwrap().from(&slab.ps); let instr = expr.compile(&slab.ps, &mut slab.cs); assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), 5.0); (|| -> Result<(),Error> { assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), 5.0); Ok(()) })().unwrap(); assert_eq!(eval_compiled!(instr, &slab, &mut ns), 5.0); #[cfg(feature="unsafe-vars")] { let x = 1.0; unsafe { slab.ps.add_unsafe_var("x".to_string(), &x) } let expr = Parser::new().parse("x", &mut slab.ps).unwrap().from(&slab.ps); let instr = expr.compile(&slab.ps, &mut slab.cs); assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), 1.0); (|| -> Result<(),Error> { assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), 1.0); Ok(()) })().unwrap(); assert_eq!(eval_compiled!(instr, &slab, &mut ns), 1.0); } Ok(()) } wrapped().unwrap(); } fasteval-0.2.4/tests/eval.rs010066400017500001750000000330151361005220000141730ustar0000000000000000use fasteval::{Evaler, Error, Slab, Cached, EmptyNamespace, CachedCallbackNamespace, Parser}; use fasteval::bool_to_f64; use std::mem; use std::collections::{BTreeMap, BTreeSet}; #[test] fn eval() { let mut slab = Slab::new(); let mut ns = BTreeMap::::new(); ns.insert("x".to_string(), 1.0); ns.insert("y".to_string(), 2.0); ns.insert("z".to_string(), 3.0); // Sanity check: assert_eq!(Parser::new().parse("3+3-3/3", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns).unwrap(), 5.0); assert_eq!(Parser::new().parse("x+y+z", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns).unwrap(), 6.0); assert_eq!(Parser::new().parse("x+y+z+a", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Err(Error::Undefined("a".to_string()))); } #[test] fn aaa_util() { assert_eq!(bool_to_f64!(true), 1.0); assert_eq!(bool_to_f64!(false), 0.0); } #[test] fn aaa_aaa_sizes() { eprintln!("sizeof(Slab):{}", mem::size_of::()); assert!(mem::size_of::()<2usize.pow(18)); // 256kB } #[test] fn aaa_aab_single() { let mut slab = Slab::new(); let mut ns = EmptyNamespace; assert_eq!(Parser::new().parse("123.456", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns).unwrap(), 123.456f64); } #[test] fn aaa_basics() { let mut slab = Slab::new(); assert_eq!( Parser::new().parse("12.34 + 43.21 + 11.11", &mut slab.ps).unwrap().from(&slab.ps).var_names(&slab), BTreeSet::new()); let mut ns = EmptyNamespace; assert_eq!( Parser::new().parse("12.34 + 43.21 + 11.11", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(66.66)); assert_eq!( Parser::new().parse("12.34 + 43.21 - 11.11", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(44.44)); assert_eq!( Parser::new().parse("11.11 * 3", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(33.33)); assert_eq!( Parser::new().parse("33.33 / 3", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(11.11)); assert_eq!( Parser::new().parse("33.33 % 3", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(0.3299999999999983)); assert_eq!( Parser::new().parse("1 and 2", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(2.0)); assert_eq!( Parser::new().parse("1 && 2", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(2.0)); assert_eq!( Parser::new().parse("2 or 0", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(2.0)); assert_eq!( Parser::new().parse("2 || 0", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(2.0)); assert_eq!( Parser::new().parse("1 > 0", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(1.0)); assert_eq!( Parser::new().parse("1 < 0", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(0.0)); assert_eq!( Parser::new().parse("+5.5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(5.5)); assert_eq!( Parser::new().parse("-5.5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(-5.5)); assert_eq!( Parser::new().parse("!5.5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(0.0)); assert_eq!( Parser::new().parse("!0", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(1.0)); assert_eq!( Parser::new().parse("(3 * 3 + 3 / 3)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(10.0)); assert_eq!( Parser::new().parse("(3 * (3 + 3) / 3)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(6.0)); assert_eq!( Parser::new().parse("4.4 + -5.5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(-1.0999999999999996)); assert_eq!( Parser::new().parse("4.4 + +5.5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(9.9)); assert_eq!( Parser::new().parse("x + 1", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Err(Error::Undefined("x".to_string()))); let mut ns = CachedCallbackNamespace::new(|_,_| Some(3.0)); assert_eq!( Parser::new().parse("x + 1", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(4.0)); assert_eq!( Parser::new().parse("1.2 + int(3.4)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(4.2)); assert_eq!( Parser::new().parse("1.2 + ceil(3.4)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(5.2)); assert_eq!( Parser::new().parse("1.2 + floor(3.4)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(4.2)); assert_eq!( Parser::new().parse("1.2 + abs(-3.4)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(4.6)); assert_eq!( Parser::new().parse("1.2 + log(1)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(1.2)); assert_eq!( Parser::new().parse("1.2 + log(10)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(2.2)); assert_eq!( Parser::new().parse("1.2 + log(0)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(std::f64::NEG_INFINITY)); assert!(Parser::new().parse("1.2 + log(-1)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns).unwrap().is_nan()); assert_eq!( Parser::new().parse("1.2 + round(3.4)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(4.2)); assert_eq!( Parser::new().parse("1.2 + round(0.5, 3.4)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(4.7)); assert_eq!( Parser::new().parse("1.2 + round(-3.4)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(-1.8)); assert_eq!( Parser::new().parse("1.2 + round(0.5, -3.4)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(-2.3)); assert_eq!( Parser::new().parse("1.2 + min(1,2,0,3.3,-1)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(0.19999999999999996)); assert_eq!( Parser::new().parse("1.2 + min(1)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(2.2)); assert_eq!( Parser::new().parse("1.2 + max(1,2,0,3.3,-1)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(4.5)); assert_eq!( Parser::new().parse("1.2 + max(1)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(2.2)); assert_eq!( Parser::new().parse(r#"12.34 + print ( 43.21, "yay" ) + 11.11"#, &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(66.66)); assert_eq!( Parser::new().parse("e()", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(2.718281828459045)); assert_eq!( Parser::new().parse("pi()", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(3.141592653589793)); assert_eq!( Parser::new().parse("sin(pi()/2)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(1.0)); assert_eq!( Parser::new().parse("cos(pi()/2)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(0.00000000000000006123233995736766)); assert_eq!( Parser::new().parse("tan(pi()/4)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(0.9999999999999999)); assert_eq!( Parser::new().parse("asin(1)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(1.5707963267948966)); assert_eq!( Parser::new().parse("acos(0)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(1.5707963267948966)); assert_eq!( Parser::new().parse("atan(1)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(0.7853981633974483)); assert_eq!( Parser::new().parse("sinh(pi()/2)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(2.3012989023072947)); assert_eq!( Parser::new().parse("cosh(pi()/2)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(2.5091784786580567)); assert_eq!( Parser::new().parse("tanh(pi()/4)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(0.6557942026326724)); } //// Commented out until we bring CachedLayeredNamespace back. // #[derive(Debug)] // struct TestEvaler; // impl Evaler for TestEvaler { // fn _var_names(&self, _slab:&Slab, _dst:&mut BTreeSet) {} // fn eval(&self, _slab:&Slab, ns:&mut impl EvalNamespace) -> Result { // match ns.lookup("x", vec![], &mut String::new()) { // Some(v) => Ok(v), // None => Ok(1.23), // } // } // } // // #[test] // fn aaa_evalns_basics() { // let slab = Slab::new(); // let mut ns = CachedLayeredNamespace::new(|_,_| Some(5.4321)); // assert_eq!({ ns.push(); let out=TestEvaler{}.eval(&slab, &mut ns); ns.pop(); out }.unwrap(), 5.4321); // ns.create_cached("x".to_string(),1.111).unwrap(); // assert_eq!({ ns.push(); let out=TestEvaler{}.eval(&slab, &mut ns); ns.pop(); out }.unwrap(), 1.111); // } #[test] fn corners() { let mut slab = Slab::new(); let mut ns = EmptyNamespace; assert_eq!( format!("{:?}", Parser::new().parse("(-1) ^ 0.5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns)), "Ok(NaN)"); } fn my_evalns_cb_function(_:&str, _:Vec) -> Option { None } #[test] fn evalns_cb_ownership() { let _ns = CachedCallbackNamespace::new(my_evalns_cb_function); let _ns = CachedCallbackNamespace::new(my_evalns_cb_function); // Conclusion: You can pass a function pointer into a function that receives ownership. let closure = |_:&str, _:Vec| None; let _ns = CachedCallbackNamespace::new(closure); let _ns = CachedCallbackNamespace::new(closure); let x = 1.0; let closure = |_:&str, _:Vec| Some(x); let _ns = CachedCallbackNamespace::new(closure); let _ns = CachedCallbackNamespace::new(closure); let mut x = 1.0; let closure = |_:&str, _:Vec| { x+=1.0; Some(x) }; let _ns = CachedCallbackNamespace::new(closure); //let _ns = CachedCallbackNamespace::new(closure); // Not allowed. // Conclusion: Functions and Closures that don't mutate state are effectively Copy. // Closures that mutate state aren't Copy. // Note that the argument type (FnMut vs Fn) doesn't actually matter, // just the implementation matters! } #[test] fn custom_func() { let mut slab = Slab::new(); let mut ns = CachedCallbackNamespace::new(|name,args| { eprintln!("In CB: {}",name); match name { "x" => Some(1.0), "y" => Some(2.0), "z" => Some(3.0), "foo" => { Some(args.get(0).unwrap_or(&std::f64::NAN)*10.0) } "bar" => { Some(args.get(0).unwrap_or(&std::f64::NAN) + args.get(1).unwrap_or(&std::f64::NAN)) } _ => None, } }); assert_eq!( Parser::new().parse("x + 1.5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, {ns.cache_clear(); &mut ns}), Ok(2.5)); assert_eq!( Parser::new().parse("x() + 1.5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, {ns.cache_clear(); &mut ns}), Ok(2.5)); assert_eq!( Parser::new().parse("x(1,2,3) + 1.5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, {ns.cache_clear(); &mut ns}), Ok(2.5)); eprintln!("I should see TWO x lookups, 1 y, and 1 z:"); assert_eq!( Parser::new().parse("x(x,y,z) + 1.5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, {ns.cache_clear(); &mut ns}), Ok(2.5)); eprintln!("I should see TWO x lookups:"); assert_eq!( Parser::new().parse("x(x,x,x) + 1.5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, {ns.cache_clear(); &mut ns}), Ok(2.5)); eprintln!("I should see TWO x lookups:"); assert_eq!( Parser::new().parse("x(1.0) + x(1.1) + x(1.0) + x(1.1)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, {ns.cache_clear(); &mut ns}), Ok(4.0)); eprintln!("---------------------------"); assert_eq!( Parser::new().parse("foo(1.23)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, {ns.cache_clear(); &mut ns}), Ok(12.3)); assert_eq!( Parser::new().parse("bar(1.23, 3.21)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, {ns.cache_clear(); &mut ns}), Ok(4.4399999999999995)); assert_eq!( format!("{:?}", Parser::new().parse("bar(1.23)", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, {ns.cache_clear(); &mut ns})), "Ok(NaN)"); } #[test] #[cfg(feature="unsafe-vars")] fn unsafe_var() { let mut slab = Slab::new(); let mut ua = 1.23; let mut ub = 4.56; unsafe { slab.ps.add_unsafe_var("ua".to_string(), &ua); slab.ps.add_unsafe_var("ub".to_string(), &ub); } let mut ns = EmptyNamespace; assert_eq!( Parser::new().parse("ua + ub + 5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(10.79)); ua+=1.0; ub+=2.0; assert_eq!( Parser::new().parse("ua + ub + 5", &mut slab.ps).unwrap().from(&slab.ps).eval(&slab, &mut ns), Ok(13.79)); let _ = (ua,ub); // Silence compiler warnings about variables not being read. } fasteval-0.2.4/tests/evalns.rs010066400017500001750000000070421361311044700145500ustar0000000000000000use fasteval::ez_eval; #[test] fn empty() { let mut ns = fasteval::EmptyNamespace; let val = ez_eval("1 + 1", &mut ns).unwrap(); assert_eq!(val, 2.0); } #[test] fn str_to_f64() { { let mut ns = fasteval::StringToF64Namespace::new(); ns.insert("a".to_string(), 1.11); ns.insert("b".to_string(), 2.22); let val = ez_eval("a + b + 1", &mut ns).unwrap(); assert_eq!(val, 4.33); } { let mut ns = fasteval::StrToF64Namespace::new(); ns.insert("a", 1.11); ns.insert("b", 2.22); let val = ez_eval("a + b + 1", &mut ns).unwrap(); assert_eq!(val, 4.33); } } #[test] fn str_to_cb() { { let mut ns = fasteval::StringToCallbackNamespace::new(); ns.insert("a".to_string(), Box::new(|args| args[0])); ns.insert("b".to_string(), Box::new(|args| args[0] * 2.0)); let val = ez_eval("a(1.11) + b(1.11) + 1", &mut ns).unwrap(); assert_eq!(val, 4.33); } { let mut ns = fasteval::StrToCallbackNamespace::new(); ns.insert("a", Box::new(|args| args[0])); ns.insert("b", Box::new(|args| args[0] * 2.0)); let val = ez_eval("a(1.11) + b(1.11) + 1", &mut ns).unwrap(); assert_eq!(val, 4.33); } } #[test] fn layered_str_to_f64() { let mut ns = fasteval::LayeredStringToF64Namespace::new(); let mut layer0 = fasteval::StringToF64Namespace::new(); layer0.insert("a".to_string(), 1.11); layer0.insert("b".to_string(), 2.22); ns.push(layer0); let val = ez_eval("a + b + 1", &mut ns).unwrap(); assert_eq!(val, 4.33); let mut layer1 = fasteval::StringToF64Namespace::new(); layer1.insert("a".to_string(), 11.11); ns.push(layer1); let val = ez_eval("a + b + 1", &mut ns).unwrap(); assert_eq!(val, 14.33); ns.pop(); let val = ez_eval("a + b + 1", &mut ns).unwrap(); assert_eq!(val, 4.33); } #[test] fn cb() { let mut ns = |name:&str, args:Vec| { match name { "a" => Some(1.11), "b" => Some(2.22), "len" => Some(args.len() as f64), _ => None, } }; let val = ez_eval("a + b + 1", &mut ns).unwrap(); assert_eq!(val, 4.33); } #[test] fn cached_cb() { let mut ns = fasteval::CachedCallbackNamespace::new(|name:&str, args:Vec| { match name { "a" => { eprintln!("cached_cb: a: This should only be printed once."); Some(1.11) } "b" => Some(2.22), "len" => Some(args.len() as f64), _ => None, } }); let val = ez_eval("a + b + 1", &mut ns).unwrap(); assert_eq!(val, 4.33); ez_eval("a + b + 1", &mut ns).unwrap(); ez_eval("a + b + 1", &mut ns).unwrap(); ez_eval("a + b + 1", &mut ns).unwrap(); ez_eval("a + b + 1", &mut ns).unwrap(); } #[test] fn custom_vector_funcs() { let vecs_cell = std::cell::RefCell::new(Vec::>::new()); let mut ns = fasteval::StrToCallbackNamespace::new(); ns.insert("x", Box::new(|_args| 2.0)); ns.insert("vec_store", Box::new(|args| { let mut vecs = vecs_cell.borrow_mut(); let index = vecs.len(); vecs.push(args); index as f64 })); ns.insert("vec_sum", Box::new(|args| { if let Some(index) = args.get(0) { if let Some(v) = vecs_cell.borrow().get(*index as usize) { return v.iter().sum(); } } std::f64::NAN })); let val = ez_eval("vec_sum(vec_store(1.1, x, 3.3)) + vec_sum(0)", &mut ns).unwrap(); assert_eq!(val, 12.8); } fasteval-0.2.4/tests/ez.rs010066400017500001750000000007561360642452000137050ustar0000000000000000use fasteval::{ez_eval, Error}; use std::collections::BTreeMap; #[test] fn ez() { assert_eq!(ez_eval("3+3-3/3", &mut BTreeMap::::new()), Ok(5.0)); assert_eq!(ez_eval("3abc+3-3/3", &mut BTreeMap::::new()), Err(Error::UnparsedTokensRemaining("abc+3-3/3".to_string()))); assert_eq!(ez_eval("z+z-z/z", &mut {let mut m=BTreeMap::::new(); m.insert("x".to_string(),1.0); m.insert("y".to_string(),2.0); m.insert("z".to_string(),3.0); m}), Ok(5.0)); } fasteval-0.2.4/tests/from_3rd.rs010066400017500001750000000127221361006101600147660ustar0000000000000000use fasteval::{Parser, Compiler, Evaler, Error, Slab, CachedCallbackNamespace, eval_compiled_ref}; use std::str::from_utf8; fn evalns_cb(name:&str, args:Vec) -> Option { match name { "w" => Some(0.0), "x" => Some(1.0), "y" => Some(2.0), "y7" => Some(2.7), "z" => Some(3.0), "foo" => Some(args[0]*10.0), "bar" => Some(args[0]+args[1]), _ => None, } } fn chk_ok(expr_str:&str, expect_compile_str:&str, expect_slab_str:&str, expect_eval:f64) { let mut slab = Slab::new(); let expr = Parser::new().parse(expr_str, &mut slab.ps).unwrap().from(&slab.ps); let instr = expr.compile(&slab.ps, &mut slab.cs); assert_eq!(format!("{:?}",instr), expect_compile_str); assert_eq!(format!("{:?}",slab), expect_slab_str); (|| -> Result<(),Error> { let mut ns = CachedCallbackNamespace::new(evalns_cb); assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), expect_eval); // Make sure Instruction eval matches normal eval: assert_eq!(eval_compiled_ref!(&instr, &slab, &mut ns), expr.eval(&slab, &mut ns).unwrap()); Ok(()) })().unwrap(); } fn chk_perr(expr_str:&str, expect_err:Error) { let mut slab = Slab::new(); let res = Parser::new().parse(expr_str, &mut slab.ps); assert_eq!(res, Err(expect_err)); } fn chk_eerr(expr_str:&str, expect_err:Error) { let mut slab = Slab::new(); let expr = Parser::new().parse(expr_str, &mut slab.ps).unwrap().from(&slab.ps); let instr = expr.compile(&slab.ps, &mut slab.cs); let mut ns = CachedCallbackNamespace::new(evalns_cb); assert_eq!(instr.eval(&slab, &mut ns), Err(expect_err)); } #[test] fn meval() { chk_perr("", Error::EofWhileParsing("value".to_string())); chk_perr("(", Error::EofWhileParsing("value".to_string())); chk_perr("0(", Error::UnparsedTokensRemaining("(".to_string())); chk_eerr("e", Error::Undefined("e".to_string())); chk_perr("1E", Error::ParseF64("1E".to_string())); chk_perr("1e+", Error::ParseF64("1e+".to_string())); chk_perr("()", Error::InvalidValue); chk_perr("2)", Error::UnparsedTokensRemaining(")".to_string())); chk_perr("2^", Error::EofWhileParsing("value".to_string())); chk_perr("(((2)", Error::EofWhileParsing("parentheses".to_string())); chk_perr("f(2,)", Error::InvalidValue); chk_perr("f(,2)", Error::InvalidValue); chk_ok("round(sin (pi()) * cos(0))", "IConst(0.0)", "Slab{ exprs:{ 0:Expression { first: EStdFunc(EFuncPi), pairs: [] }, 1:Expression { first: EConstant(0.0), pairs: [] }, 2:Expression { first: EStdFunc(EFuncSin(ExpressionI(0))), pairs: [ExprPair(EMul, EStdFunc(EFuncCos(ExpressionI(1))))] }, 3:Expression { first: EStdFunc(EFuncRound { modulus: None, expr: ExpressionI(2) }), pairs: [] } }, vals:{}, instrs:{} }", 0.0); chk_ok("max(1.)", "IConst(1.0)", "Slab{ exprs:{ 0:Expression { first: EConstant(1.0), pairs: [] }, 1:Expression { first: EStdFunc(EFuncMax { first: ExpressionI(0), rest: [] }), pairs: [] } }, vals:{}, instrs:{} }", 1.0); chk_ok("max(1., 2., -1)", "IConst(2.0)", "Slab{ exprs:{ 0:Expression { first: EConstant(1.0), pairs: [] }, 1:Expression { first: EConstant(2.0), pairs: [] }, 2:Expression { first: EConstant(-1.0), pairs: [] }, 3:Expression { first: EStdFunc(EFuncMax { first: ExpressionI(0), rest: [ExpressionI(1), ExpressionI(2)] }), pairs: [] } }, vals:{}, instrs:{} }", 2.0); chk_ok("sin(1.) + cos(2.)", "IConst(0.4253241482607541)", "Slab{ exprs:{ 0:Expression { first: EConstant(1.0), pairs: [] }, 1:Expression { first: EConstant(2.0), pairs: [] }, 2:Expression { first: EStdFunc(EFuncSin(ExpressionI(0))), pairs: [ExprPair(EAdd, EStdFunc(EFuncCos(ExpressionI(1))))] } }, vals:{}, instrs:{} }", (1f64).sin() + (2f64).cos()); } #[test] fn overflow_stack() { chk_perr(from_utf8(&[b'('; 1]).unwrap(), Error::EofWhileParsing("value".to_string())); chk_perr(from_utf8(&[b'('; 2]).unwrap(), Error::EofWhileParsing("value".to_string())); chk_perr(from_utf8(&[b'('; 4]).unwrap(), Error::EofWhileParsing("value".to_string())); chk_perr(from_utf8(&[b'('; 8]).unwrap(), Error::EofWhileParsing("value".to_string())); chk_perr(from_utf8(&[b'('; 16]).unwrap(), Error::EofWhileParsing("value".to_string())); chk_perr(from_utf8(&[b'('; 32]).unwrap(), Error::EofWhileParsing("value".to_string())); chk_perr(from_utf8(&[b'('; 33]).unwrap(), Error::TooDeep); chk_perr(from_utf8(&[b'('; 64]).unwrap(), Error::TooDeep); chk_perr(from_utf8(&[b'('; 128]).unwrap(), Error::TooDeep); chk_perr(from_utf8(&[b'('; 256]).unwrap(), Error::TooDeep); chk_perr(from_utf8(&[b'('; 512]).unwrap(), Error::TooDeep); chk_perr(from_utf8(&[b'('; 1024]).unwrap(), Error::TooDeep); chk_perr(from_utf8(&[b'('; 2048]).unwrap(), Error::TooDeep); chk_perr(from_utf8(&[b'('; 4096]).unwrap(), Error::TooDeep); chk_perr(from_utf8(&[b'('; 8192]).unwrap(), Error::TooLong); // Test custom safety parse limits: assert_eq!(Parser{expr_len_limit:fasteval::parser::DEFAULT_EXPR_LEN_LIMIT, expr_depth_limit:31}.parse( from_utf8(&[b'('; 32]).unwrap(), &mut Slab::new().ps ), Err(Error::TooDeep)); assert_eq!(Parser{expr_len_limit:8, expr_depth_limit:fasteval::parser::DEFAULT_EXPR_DEPTH_LIMIT}.parse( from_utf8(&[b'('; 32]).unwrap(), &mut Slab::new().ps ), Err(Error::TooLong)); } fasteval-0.2.4/tests/from_go.rs010066400017500001750000000331331361005225700147110ustar0000000000000000// This is a battery of unit tests taken directly from my original Go project. // I know the test names suck, but I'm not going to change them because I want line-for-line compatibility with the Go tests. use fasteval::{Evaler, ExpressionI, Parser, Error, Slab, EmptyNamespace, CachedCallbackNamespace}; use std::collections::BTreeMap; use std::collections::BTreeSet; fn parse_raw<'a>(s:&str, slab:&'a mut Slab) -> Result { Parser::new().parse(s, &mut slab.ps) } fn ok_parse<'a>(s:&str, slab:&'a mut Slab) -> ExpressionI { parse_raw(s,slab).unwrap() } fn do_eval(s:&str) -> f64 { let mut slab = Slab::new(); let mut ns = EmptyNamespace; ok_parse(s, &mut slab).from(&slab.ps).eval(&slab, &mut ns).unwrap() } //// TODO: // fn capture_stderr(f:&dyn Fn()) -> String { // f(); // "".to_string() // } #[test] fn aaa_test_a() { let mut slab = Slab::new(); ok_parse("3", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [] } }, vals:{}, instrs:{} }"); ok_parse("3.14", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.14), pairs: [] } }, vals:{}, instrs:{} }"); ok_parse("3+5", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EAdd, EConstant(5.0))] } }, vals:{}, instrs:{} }"); ok_parse("3-5", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(ESub, EConstant(5.0))] } }, vals:{}, instrs:{} }"); ok_parse("3*5", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EMul, EConstant(5.0))] } }, vals:{}, instrs:{} }"); ok_parse("3/5", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EDiv, EConstant(5.0))] } }, vals:{}, instrs:{} }"); ok_parse("3^5", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EExp, EConstant(5.0))] } }, vals:{}, instrs:{} }"); } #[test] fn aaa_test_b0() { let mut slab = Slab::new(); ok_parse("3.14 + 4.99999999999999", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.14), pairs: [ExprPair(EAdd, EConstant(4.99999999999999))] } }, vals:{}, instrs:{} }"); ok_parse("3.14 + 4.99999999999999999999999999999999999999999999999999999", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.14), pairs: [ExprPair(EAdd, EConstant(5.0))] } }, vals:{}, instrs:{} }"); // Go can parse this, but not Rust: assert_eq!(parse_raw("3.14 + 4.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", &mut slab), Err(Error::ParseF64("4.999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999".to_string()))); ok_parse("3.14 + 0.9999", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.14), pairs: [ExprPair(EAdd, EConstant(0.9999))] } }, vals:{}, instrs:{} }"); ok_parse("3.14 + .9999", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.14), pairs: [ExprPair(EAdd, EConstant(0.9999))] } }, vals:{}, instrs:{} }"); ok_parse("3.14 + 0.", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.14), pairs: [ExprPair(EAdd, EConstant(0.0))] } }, vals:{}, instrs:{} }"); } #[test] fn aaa_test_b1() { let mut slab = Slab::new(); assert_eq!(parse_raw("3.14 + 4.99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.9999", &mut slab), Err(Error::ParseF64("4.99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.9999".to_string()))); assert_eq!(parse_raw("3.14 + 4.9999.9999", &mut slab), Err(Error::ParseF64("4.9999.9999".to_string()))); } #[test] fn aaa_test_b2() { let mut slab = Slab::new(); assert_eq!(parse_raw("3.14 + .", &mut slab), Err(Error::ParseF64(".".to_string()))); } #[test] fn aaa_test_c0() { let mut slab = Slab::new(); ok_parse("3+5-xyz", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EAdd, EConstant(5.0)), ExprPair(ESub, EStdFunc(EVar(\"xyz\")))] } }, vals:{}, instrs:{} }"); ok_parse("3+5-xyz_abc_def123", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EAdd, EConstant(5.0)), ExprPair(ESub, EStdFunc(EVar(\"xyz_abc_def123\")))] } }, vals:{}, instrs:{} }"); ok_parse("3+5-XYZ_abc_def123", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EAdd, EConstant(5.0)), ExprPair(ESub, EStdFunc(EVar(\"XYZ_abc_def123\")))] } }, vals:{}, instrs:{} }"); ok_parse("3+5-XYZ_ab*c_def123", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EAdd, EConstant(5.0)), ExprPair(ESub, EStdFunc(EVar(\"XYZ_ab\"))), ExprPair(EMul, EStdFunc(EVar(\"c_def123\")))] } }, vals:{}, instrs:{} }"); } #[test] fn aaa_test_c1() { let mut slab = Slab::new(); assert_eq!(parse_raw("3+5-XYZ_ab~c_def123", &mut slab), Err(Error::UnparsedTokensRemaining("~c_def123".to_string()))); } #[test] fn aaa_test_d0() { let mut slab = Slab::new(); ok_parse("3+(-5)", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(-5.0), pairs: [] }, 1:Expression { first: EConstant(3.0), pairs: [ExprPair(EAdd, EUnaryOp(EParentheses(ExpressionI(0))))] } }, vals:{}, instrs:{} }"); ok_parse("3+-5", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EAdd, EConstant(-5.0))] } }, vals:{}, instrs:{} }"); ok_parse("3++5", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EAdd, EConstant(5.0))] } }, vals:{}, instrs:{} }"); ok_parse(" 3 + ( -x + y ) ", &mut slab); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EUnaryOp(ENeg(ValueI(0))), pairs: [ExprPair(EAdd, EStdFunc(EVar(\"y\")))] }, 1:Expression { first: EConstant(3.0), pairs: [ExprPair(EAdd, EUnaryOp(EParentheses(ExpressionI(0))))] } }, vals:{ 0:EStdFunc(EVar(\"x\")) }, instrs:{} }"); } #[test] fn aaa_test_d1() { let mut slab = Slab::new(); assert_eq!(parse_raw(" 3 + ( -x + y ", &mut slab), Err(Error::EofWhileParsing("parentheses".to_string()))); } #[test] fn aaa_test_e() { assert_eq!(do_eval("3"), 3.0); assert_eq!(do_eval("3+4+5+6"), 18.0); assert_eq!(do_eval("3-4-5-6"), -12.0); assert_eq!(do_eval("3*4*5*6"), 360.0); assert_eq!(do_eval("3/4/5/6"), 0.024999999999999998); // Fragile! assert_eq!(do_eval("2^3^4"), 2417851639229258349412352.0); assert_eq!(do_eval("3*3-3/3"), 8.0); assert_eq!(do_eval("(1+1)^3"), 8.0); assert_eq!(do_eval("(1+(-1)^4)^3"), 8.0); assert_eq!(do_eval("(1+1)^(1+1+1)"), 8.0); assert_eq!(do_eval("(8^(1/3))^(3)"), 8.0); // Fragile! Go is 7.999999999999997 assert_eq!(do_eval("round( (8^(1/3))^(3) )"), 8.0); assert_eq!(do_eval("5%2"), 1.0); assert_eq!(do_eval("5%3"), 2.0); assert_eq!(do_eval("5.1%3.2"), 1.8999999999999995); assert_eq!(do_eval("5.1%2.5"), 0.09999999999999964); assert_eq!(do_eval("5.1%2.499999999"), 0.10000000199999981); assert_eq!(do_eval("-5%2"), -1.0); assert_eq!(do_eval("-5%3"), -2.0); assert_eq!(do_eval("-5.1%3.2"), -1.8999999999999995); assert_eq!(do_eval("-5.1%2.5"), -0.09999999999999964); assert_eq!(do_eval("-5.1%2.499999999"), -0.10000000199999981); assert_eq!(do_eval("5%-2"), 1.0); assert_eq!(do_eval("5%-3"), 2.0); assert_eq!(do_eval("5.1%-3.2"), 1.8999999999999995); assert_eq!(do_eval("5.1%-2.5"), 0.09999999999999964); assert_eq!(do_eval("5.1%-2.499999999"), 0.10000000199999981); assert_eq!(do_eval("int(5)%int(2)"), 1.0); assert_eq!(do_eval("int(5)%int(3)"), 2.0); assert_eq!(do_eval("int(5.1)%round(3.2)"), 2.0); assert_eq!(do_eval("int(5.1)%round(2.5)"), 2.0); assert_eq!(do_eval("int(5.1)%round(2.499999999)"), 1.0); assert_eq!(do_eval("int(5)%int(-2)"), 1.0); assert_eq!(do_eval("int(5)%int(-3)"), 2.0); assert_eq!(do_eval("int(5.1)%round(-3.2)"), 2.0); assert_eq!(do_eval("int(5.1)%round(-2.5)"), 2.0); assert_eq!(do_eval("int(5.1)%round(-2.499999999)"), 1.0); assert_eq!(do_eval("int(123.456/78)*78 + 123.456%78"), 123.456); assert_eq!(do_eval("int(-123.456/78)*78 + -123.456%78"), -123.456); assert_eq!(do_eval("int(-123.456/-78)*-78 + -123.456%-78"), -123.456); } #[test] fn aaa_test_f() { let mut slab = Slab::new(); assert_eq!(ok_parse("(x)^(3)", &mut slab).from(&slab.ps).eval(&slab, &mut CachedCallbackNamespace::new(|n,_| { [("x",2.0)].iter().cloned().collect::>().get(n).cloned() })).unwrap(), 8.0); assert_eq!(ok_parse("(x)^(y)", &mut slab).from(&slab.ps).eval(&slab, &mut CachedCallbackNamespace::new(|n,_| { [("x",2.0),("y",3.0)].iter().cloned().collect::>().get(n).cloned() })).unwrap(), 8.0); assert_eq!(ok_parse("(x)^(y)", &mut slab).from(&slab.ps).var_names(&slab).len(), 2); assert_eq!(ok_parse("1+(x*y/2)^(z)", &mut slab).from(&slab.ps).var_names(&slab).len(), 3); assert_eq!(format!("{:?}",ok_parse("1+(x*y/2)^(z)", &mut slab).from(&slab.ps).var_names(&slab).iter().collect::>()), r#"{"x", "y", "z"}"#); assert_eq!(format!("{:?}",ok_parse("1+(x/y/2)^(z)", &mut slab).from(&slab.ps).var_names(&slab).iter().collect::>()), r#"{"x", "y", "z"}"#); // Test a division-by-0 during VariableNames() assert_eq!(format!("{}",do_eval("1/0")), "inf"); // Test an explicit division-by-0. Go says "+Inf". } #[test] fn aaa_test_g() { assert_eq!(do_eval("2k"), 2000.0); assert_eq!(do_eval("2K"), 2000.0); assert_eq!(do_eval("2.10M"), 2.1e+06); assert_eq!(do_eval("2.10G"), 2.1e+09); assert_eq!(do_eval("2.10T"), 2.1e+12); assert_eq!(do_eval("2.10m"), 2.1e-03); assert_eq!(do_eval("2.10u"), 2.1e-06); assert_eq!(do_eval("2.10µ"), 2.1e-06); assert_eq!(do_eval("2.10n"), 2.1e-09); assert_eq!(do_eval("2.10p"), 2.1e-12); } #[test] fn aaa_test_h() { assert_eq!(do_eval("!100"), 0.0); assert_eq!(do_eval("!0"), 1.0); assert_eq!(do_eval("!(1-1)"), 1.0); } #[test] fn aaa_test_i() { assert_eq!(do_eval("1<2"), 1.0); assert_eq!(do_eval("(1+2)<2"), 0.0); assert_eq!(do_eval("2<=2"), 1.0); assert_eq!(do_eval("2<=(2-0.1)"), 0.0); assert_eq!(do_eval("2k==2K"), 1.0); assert_eq!(do_eval("2k==2000"), 1.0); assert_eq!(do_eval("2k==2000.0000001"), 0.0); assert_eq!(do_eval("2k!=2000.0000001"), 1.0); assert_eq!(do_eval("2k!=3G"), 1.0); assert_eq!(do_eval("1000*2k!=2M"), 0.0); assert_eq!(do_eval("3>=2"), 1.0); assert_eq!(do_eval("3>=2^2"), 0.0); assert_eq!(do_eval("3>2"), 1.0); assert_eq!(do_eval("3>2^2"), 0.0); assert_eq!(do_eval("1 or 1"), 1.0); assert_eq!(do_eval("1 || 1"), 1.0); assert_eq!(do_eval("1 or 0"), 1.0); assert_eq!(do_eval("1 || 0"), 1.0); assert_eq!(do_eval("0 or 1"), 1.0); assert_eq!(do_eval("0 || 1"), 1.0); assert_eq!(do_eval("0 or 0"), 0.0); assert_eq!(do_eval("0 || 0"), 0.0); assert_eq!(do_eval("0 and 0"), 0.0); assert_eq!(do_eval("0 && 0"), 0.0); assert_eq!(do_eval("0 and 1"), 0.0); assert_eq!(do_eval("0 && 1"), 0.0); assert_eq!(do_eval("1 and 0"), 0.0); assert_eq!(do_eval("1 && 0"), 0.0); assert_eq!(do_eval("1 and 1"), 1.0); assert_eq!(do_eval("1 && 1"), 1.0); assert_eq!(do_eval("(2k*1k==2M and 3/2<2 or 0^2) and !(1-1)"), 1.0); assert_eq!(do_eval("(2k*1k==2M && 3/2<2 || 0^2) && !(1-1)"), 1.0); // Ternary ability: assert_eq!(do_eval("2 and 3"), 3.0); assert_eq!(do_eval("2 && 3"), 3.0); assert_eq!(do_eval("2 or 3"), 2.0); assert_eq!(do_eval("2 || 3"), 2.0); assert_eq!(do_eval("2 and 3 or 4"), 3.0); assert_eq!(do_eval("2 && 3 || 4"), 3.0); assert_eq!(do_eval("0 and 3 or 4"), 4.0); assert_eq!(do_eval("0 && 3 || 4"), 4.0); assert_eq!(do_eval("2 and 0 or 4"), 4.0); assert_eq!(do_eval("2 && 0 || 4"), 4.0); assert_eq!(do_eval("0 and 3 or 0 and 5 or 6"), 6.0); assert_eq!(do_eval("0 && 3 || 0 && 5 || 6"), 6.0); } #[test] fn aaa_test_j() { assert_eq!(do_eval("2/3*3/2"), 1.0); assert_eq!(do_eval("2%3*3/2"), 3.0); assert_eq!(do_eval("3^2%2^2*2^2/3^2"), 0.4444444444444444); assert_eq!(do_eval("1+2-3+4"), 4.0); } #[test] fn aaa_test_k() { do_eval(r#"print("a",print("b",print("c",5,"C"),"B"),"A")"#); // TODO: Capture -- i bet i can re-use rust's existing test-output capturing feature: // stderr:=CaptureStderr(func(){ // do_eval(r#"print("a",print("b",print("c",5,"C"),"B"),"A")"#); // Other stuff process from-inside-to-out. // }) // stderr=strings.TrimSpace(stderr) // Assert(stderr==`c 5 C //b 5 B //a 5 A`) } fasteval-0.2.4/tests/parse.rs010066400017500001750000000250151361005175000143700ustar0000000000000000use fasteval::{Error, Slab, Parser}; #[test] fn basics() { let mut slab = Slab::new(); Parser::new().parse("12.34 + 43.21 + 11.11", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(12.34), pairs: [ExprPair(EAdd, EConstant(43.21)), ExprPair(EAdd, EConstant(11.11))] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34 + abs ( -43 - 0.21 ) + 11.11", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(-43.0), pairs: [ExprPair(ESub, EConstant(0.21))] }, 1:Expression { first: EConstant(12.34), pairs: [ExprPair(EAdd, EStdFunc(EFuncAbs(ExpressionI(0)))), ExprPair(EAdd, EConstant(11.11))] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34 + abs [ -43 - 0.21 ] + 11.11", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(-43.0), pairs: [ExprPair(ESub, EConstant(0.21))] }, 1:Expression { first: EConstant(12.34), pairs: [ExprPair(EAdd, EStdFunc(EFuncAbs(ExpressionI(0)))), ExprPair(EAdd, EConstant(11.11))] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34 + print ( 43.21 ) + 11.11", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(43.21), pairs: [] }, 1:Expression { first: EConstant(12.34), pairs: [ExprPair(EAdd, EPrintFunc(PrintFunc([EExpr(ExpressionI(0))]))), ExprPair(EAdd, EConstant(11.11))] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34 + print [ 43.21 ] + 11.11", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(43.21), pairs: [] }, 1:Expression { first: EConstant(12.34), pairs: [ExprPair(EAdd, EPrintFunc(PrintFunc([EExpr(ExpressionI(0))]))), ExprPair(EAdd, EConstant(11.11))] } }, vals:{}, instrs:{} }"); Parser::new().parse("(-1) ^ 0.5", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(-1.0), pairs: [] }, 1:Expression { first: EUnaryOp(EParentheses(ExpressionI(0))), pairs: [ExprPair(EExp, EConstant(0.5))] } }, vals:{}, instrs:{} }"); Parser::new().parse("(1 + [2 - (3 * 4) / 5] ^ 6) % 7", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(3.0), pairs: [ExprPair(EMul, EConstant(4.0))] }, 1:Expression { first: EConstant(2.0), pairs: [ExprPair(ESub, EUnaryOp(EParentheses(ExpressionI(0)))), ExprPair(EDiv, EConstant(5.0))] }, 2:Expression { first: EConstant(1.0), pairs: [ExprPair(EAdd, EUnaryOp(EParentheses(ExpressionI(1)))), ExprPair(EExp, EConstant(6.0))] }, 3:Expression { first: EUnaryOp(EParentheses(ExpressionI(2))), pairs: [ExprPair(EMod, EConstant(7.0))] } }, vals:{}, instrs:{} }"); } #[test] fn consts() { let mut slab = Slab::new(); Parser::new().parse("12.34", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(12.34), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse(".34", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(0.34), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(12.0), pairs: [] } }, vals:{}, instrs:{} }"); assert_eq!(Parser::new().parse(".", &mut slab.ps), Err(Error::ParseF64(".".to_string()))); assert_eq!(Parser::new().parse("12..34", &mut slab.ps), Err(Error::ParseF64("12..34".to_string()))); Parser::new().parse("12.34k", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(12340.0), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34K", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(12340.0), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34M", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(12340000.0), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34G", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(12340000000.0), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34T", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(12340000000000.0), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34m", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(0.01234), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34u", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(0.00001234), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34µ", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(0.00001234), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34n", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(0.00000001234), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34p", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(0.00000000001234), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34e56", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(1234000000000000000000000000000000000000000000000000000000.0), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34e+56", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(1234000000000000000000000000000000000000000000000000000000.0), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34E56", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(1234000000000000000000000000000000000000000000000000000000.0), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34E+56", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(1234000000000000000000000000000000000000000000000000000000.0), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34e-56", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(0.0000000000000000000000000000000000000000000000000000001234), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("12.34E-56", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(0.0000000000000000000000000000000000000000000000000000001234), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("+12.34E-56", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(0.0000000000000000000000000000000000000000000000000000001234), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("-12.34E-56", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(-0.0000000000000000000000000000000000000000000000000000001234), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("-x", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EUnaryOp(ENeg(ValueI(0))), pairs: [] } }, vals:{ 0:EStdFunc(EVar(\"x\")) }, instrs:{} }"); Parser::new().parse("NaN", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(NaN), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("+NaN", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(NaN), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("-NaN", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(NaN), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("inf", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(inf), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("+inf", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(inf), pairs: [] } }, vals:{}, instrs:{} }"); Parser::new().parse("-inf", &mut slab.ps).unwrap(); assert_eq!(format!("{:?}",&slab), "Slab{ exprs:{ 0:Expression { first: EConstant(-inf), pairs: [] } }, vals:{}, instrs:{} }"); assert_eq!(Parser::new().parse("-infK", &mut slab.ps), Err(Error::UnparsedTokensRemaining("K".to_string()))); assert_eq!(Parser::new().parse("NaNK", &mut slab.ps), Err(Error::UnparsedTokensRemaining("K".to_string()))); assert_eq!(Parser::new().parse("12.34e56K", &mut slab.ps), Err(Error::UnparsedTokensRemaining("K".to_string()))); } #[test] #[cfg(feature="unsafe-vars")] fn unsafe_var() { fn replace_addrs(mut s:String) -> String { let mut start=0; loop { match s[start..].find(" 0x") { None => break, Some(i) => { let v = unsafe { s.as_mut_vec() }; start = start+i+3; loop { match v.get(start) { None => break, Some(&b) => { if (b'0'<=b && b<=b'9') || (b'a'<=b && b<=b'f') { v[start]=b'?'; start+=1; } else { break; } } } } } }; } s } let mut slab = Slab::new(); let ua = 1.23; let ub = 4.56; unsafe { slab.ps.add_unsafe_var("ua".to_string(), &ua); slab.ps.add_unsafe_var("ub".to_string(), &ub); } Parser::new().parse("ua + ub + 5", &mut slab.ps).unwrap(); assert_eq!(replace_addrs(format!("{:?}",&slab)), "Slab{ exprs:{ 0:Expression { first: EStdFunc(EUnsafeVar { name: \"ua\", ptr: 0x???????????? }), pairs: [ExprPair(EAdd, EStdFunc(EUnsafeVar { name: \"ub\", ptr: 0x???????????? })), ExprPair(EAdd, EConstant(5.0))] } }, vals:{}, instrs:{} }"); } fasteval-0.2.4/.cargo_vcs_info.json0000644000000001121361311252600127340ustar00{ "git": { "sha1": "a5e9bac2c3623a38bf68b7614ec56a6d6bd60814" } } fasteval-0.2.4/Cargo.lock0000644000000002141361311252600107120ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "fasteval" version = "0.2.4"