pax_global_header00006660000000000000000000000064133273171460014521gustar00rootroot0000000000000052 comment=77f06e2ceaff262f739e00958a5d7558d131ca75 fparserc++-4.5.2/000077500000000000000000000000001332731714600134645ustar00rootroot00000000000000fparserc++-4.5.2/Makefile000066400000000000000000000312421332731714600151260ustar00rootroot00000000000000#=========================================================================== # This Makefile uses quite heavily GNU Make extensions, so it's probably # hopeless to try to use it with other Make programs which do not have the # same extensions. # # Also requires: rm, grep, sed and g++ (regardless of what CXX and LD are). # The optimizer code generator requires bison. #=========================================================================== RELEASE_VERSION=4.5.2 # The FP_FEATURE_FLAGS is set by run_full_release_testing.sh, but can be # used otherwise as well. ifeq ($(FP_FEATURE_FLAGS),) FEATURE_FLAGS = FEATURE_FLAGS += -DFP_ENABLE_EVAL #FEATURE_FLAGS += -DFP_NO_SUPPORT_OPTIMIZER #FEATURE_FLAGS += -DFP_USE_THREAD_SAFE_EVAL #FEATURE_FLAGS += -DFP_USE_THREAD_SAFE_EVAL_WITH_ALLOCA #FEATURE_FLAGS += -D_GLIBCXX_DEBUG #FEATURE_FLAGS += -DFP_DISABLE_SHORTCUT_LOGICAL_EVALUATION FEATURE_FLAGS += -DFP_SUPPORT_FLOAT_TYPE FEATURE_FLAGS += -DFP_SUPPORT_LONG_DOUBLE_TYPE FEATURE_FLAGS += -DFP_SUPPORT_LONG_INT_TYPE #FEATURE_FLAGS += -DFP_SUPPORT_MPFR_FLOAT_TYPE #FEATURE_FLAGS += -DFP_SUPPORT_GMP_INT_TYPE FEATURE_FLAGS += -DFP_SUPPORT_COMPLEX_DOUBLE_TYPE FEATURE_FLAGS += -DFP_SUPPORT_COMPLEX_FLOAT_TYPE FEATURE_FLAGS += -DFP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE FEATURE_FLAGS += -DFP_USE_STRTOLD #FEATURE_FLAGS += -DFP_SUPPORT_CPLUSPLUS11_MATH_FUNCS else FEATURE_FLAGS = $(FP_FEATURE_FLAGS) endif OPTIMIZATION=-O3 -ffast-math -march=native # -ffunction-sections -fdata-sections # For GCC (not clang): OPTIMIZATION += -fexpensive-optimizations -fvpt -fomit-frame-pointer -ffunction-cse #OPTIMIZATION+=-g #OPTIMIZATION=-g -O0 -fno-inline #OPTIMIZATION=-g -O0 -fno-inline -fno-inline-functions -fno-default-inline #OPTIMIZATION=-g -O2 -fno-inline -fno-inline-functions -fno-default-inline #OPTIMIZATION=-g -pg -fprofile -fprofile-values -fprofile-generate -ftest-coverage #OPTIMIZATION=-g -pg CXX=g++ #CXX=clang++ # -m32 -mfpmath=sse # -m32 -mfpmath=387 LD=g++ #LD=clang++ #LD=g++ -g # -m32 -mfpmath=sse # -m32 -mfpmath=387 #OPTIMIZATION += -finline -finline-functions -fdefault-inline #OPTIMIZATION += -finline-limit=300000 #OPTIMIZATION += --param max-inline-insns-auto=300000 #OPTIMIZATION += --param max-inline-recursive-depth-auto=30 #OPTIMIZATION += --param max-inline-insns-single=300000 #OPTIMIZATION += --param inline-unit-growth=9000 #OPTIMIZATION += --param max-early-inliner-iterations=30 #OPTIMIZATION += --param early-inlining-insns=90000 #OPTIMIZATION += -fkeep-inline-functions #OPTIMIZATION += -fimplement-inlines FEATURE_FLAGS += -DFUNCTIONPARSER_SUPPORT_DEBUGGING #LD += -fprofile -fprofile-values -fprofile-generate -ftest-coverage CPPFLAGS=$(FEATURE_FLAGS) CXXFLAGS=-Wall -W -Wno-long-long -pedantic -ansi $(OPTIMIZATION) #CXXFLAGS += -Wunreachable-code #CXXFLAGS += -std=c++0x #CXXFLAGS += -Weffc++ ifneq (,$(findstring -DFP_SUPPORT_MPFR_FLOAT_TYPE,$(FEATURE_FLAGS))) LDFLAGS += -lgmp -lmpfr ADDITIONAL_MODULES = mpfr/MpfrFloat.o ifneq (,$(findstring -DFP_SUPPORT_GMP_INT_TYPE,$(FEATURE_FLAGS))) ADDITIONAL_MODULES += mpfr/GmpInt.o endif else ifneq (,$(findstring -DFP_SUPPORT_GMP_INT_TYPE,$(FEATURE_FLAGS))) LDFLAGS += -lgmp ADDITIONAL_MODULES = mpfr/GmpInt.o endif endif ifneq (,$(findstring -DFP_USE_THREAD_SAFE_EVAL,$(FEATURE_FLAGS))) BOOST_THREAD_LIB = -lboost_thread-mt -lboost_system else ifneq (,$(findstring -DFP_USE_THREAD_SAFE_EVAL_WITH_ALLOCA,$(FEATURE_FLAGS))) BOOST_THREAD_LIB = -lboost_thread-mt -lboost_system endif endif ifneq (,$(findstring -DFP_SUPPORT_CPLUSPLUS11_MATH_FUNCS,$(FEATURE_FLAGS))) CXXFLAGS += -std=c++0x endif #LD += -Xlinker --gc-sections #LD += -Xlinker --print-gc-sections # ^Use this option to list everything that GC removed. # For compilation with ICC: #OPTIMIZATION=-O3 -xT -inline-level=2 -w1 -openmp -mssse3 #CXX=icc #LD=icc -L/opt/intel/Compiler/11.1/059/bin/intel64/lib -lirc -lstdc++ -openmp -lguide -lpthread #CXXFLAGS=-Wall $(OPTIMIZATION) $(FEATURE_FLAGS) CPPFLAGS += -I"`pwd`" all: testbed speedtest functioninfo FP_MODULES = fparser.o \ fpoptimizer/grammar_data.o \ fpoptimizer/optimize_main.o \ fpoptimizer/readbytecode.o \ fpoptimizer/makebytecode.o \ fpoptimizer/codetree.o \ fpoptimizer/grammar.o \ fpoptimizer/optimize.o \ fpoptimizer/optimize_match.o \ fpoptimizer/optimize_synth.o \ fpoptimizer/optimize_debug.o \ fpoptimizer/constantfolding.o \ fpoptimizer/valuerange.o \ fpoptimizer/rangeestimation.o \ fpoptimizer/opcodename.o \ fpoptimizer/bytecodesynth.o \ fpoptimizer/transformations.o \ fpoptimizer/cse.o \ fpoptimizer/debug.o \ fpoptimizer/hash.o \ $(ADDITIONAL_MODULES) RELEASE_PACK_FILES = examples/example.cc examples/example2.cc fparser.cc \ fparser.hh fparser_mpfr.hh fparser_gmpint.hh \ fpoptimizer.cc fpconfig.hh extrasrc/fptypes.hh extrasrc/fpaux.hh \ mpfr/MpfrFloat.hh mpfr/MpfrFloat.cc mpfr/GmpInt.hh mpfr/GmpInt.cc \ extrasrc/fp_opcode_add.inc \ extrasrc/fp_identifier_parser.inc \ docs/fparser.html docs/style.css docs/lgpl.txt docs/gpl.txt testbed: testbed.o $(FP_MODULES) $(LD) -o $@ $^ $(LDFLAGS) $(BOOST_THREAD_LIB) fpoptimizer.o: fpoptimizer.cc testbed_release: testbed.o fparser.o fpoptimizer.o $(ADDITIONAL_MODULES) $(LD) -o $@ $^ $(LDFLAGS) $(BOOST_THREAD_LIB) speedtest: util/speedtest.o $(FP_MODULES) $(LD) -o $@ $^ $(LDFLAGS) speedtest_release: util/speedtest.o fparser.o fpoptimizer.o $(LD) -o $@ $^ $(LDFLAGS) examples/example: examples/example.o $(FP_MODULES) $(LD) -o $@ $^ $(LDFLAGS) examples/example2: examples/example2.o $(FP_MODULES) $(LD) -o $@ $^ $(LDFLAGS) ftest: util/ftest.o $(FP_MODULES) $(LD) -o $@ $^ $(LDFLAGS) powi_speedtest: util/powi_speedtest.o $(FP_MODULES) $(LD) -o $@ $^ $(LDFLAGS) koe: koe.o $(FP_MODULES) $(LD) -o $@ $^ $(LDFLAGS) functioninfo: util/functioninfo.o $(FP_MODULES) $(LD) -o $@ $^ $(LDFLAGS) fpoptimizer/grammar_data.cc: \ util/tree_grammar_parser \ fpoptimizer/treerules.dat util/tree_grammar_parser < fpoptimizer/treerules.dat > $@ extrasrc/fp_opcode_add.inc: \ util/bytecoderules_parser \ util/bytecoderules.dat \ util/bytecoderules_header.txt \ util/cpp_compress cat util/bytecoderules_header.txt > $@ util/bytecoderules_parser \ < util/bytecoderules.dat \ | util/cpp_compress \ >> $@ tests/make_tests: \ tests/make_tests.o util/cpp_compress.o $(LD) -o $@ $^ $(LDFLAGS) testbed_tests.inc: tests/make_tests tests/make_tests tests/*/* -o $@ FPOPTIMIZER_CC_FILES=\ lib/crc32.hh \ lib/autoptr.hh \ lib/functional.hh \ fpoptimizer/hash.hh \ fpoptimizer/codetree.hh \ fpoptimizer/grammar.hh \ fpoptimizer/consts.hh \ fpoptimizer/optimize.hh \ fpoptimizer/opcodename.hh \ fpoptimizer/opcodename.cc \ fpoptimizer/bytecodesynth.hh \ fpoptimizer/bytecodesynth.cc \ fpoptimizer/valuerange.hh \ fpoptimizer/rangeestimation.hh \ fpoptimizer/constantfolding.hh \ fpoptimizer/logic_boolgroups.hh \ fpoptimizer/logic_collections.hh \ fpoptimizer/logic_ifoperations.hh \ fpoptimizer/logic_powoperations.hh \ fpoptimizer/logic_comparisons.hh \ fpoptimizer/codetree.cc \ fpoptimizer/debug.cc \ fpoptimizer/grammar.cc \ fpoptimizer/grammar_data.cc \ fpoptimizer/optimize.cc \ fpoptimizer/optimize_match.cc \ fpoptimizer/optimize_synth.cc \ fpoptimizer/optimize_debug.cc \ fpoptimizer/hash.cc \ fpoptimizer/makebytecode.cc \ fpoptimizer/readbytecode.cc \ fpoptimizer/constantfolding.cc \ fpoptimizer/valuerange.cc \ fpoptimizer/rangeestimation.cc \ fpoptimizer/transformations.cc \ fpoptimizer/cse.cc \ fpoptimizer/optimize_main.cc fpoptimizer.cc: fpoptimizer/fpoptimizer_header.txt \ fpoptimizer/fpoptimizer_footer.txt \ $(FPOPTIMIZER_CC_FILES) \ util/cpp_compress rm -f fpoptimizer.cc cat fpoptimizer/fpoptimizer_header.txt > $@ for file in $(FPOPTIMIZER_CC_FILES); do \ echo "#line 1 \"$$file\""; \ sed -r "s@^(#include \".*)@// line removed for fpoptimizer.cc: \\1@" < "$$file"; \ echo; \ done | sed 's@BEGIN_EXPLICIT_INSTANTATION.*@@;s@.*END_EXPLICIT_INSTANTATION@@' \ | util/cpp_compress "lnxyceti" >> $@ # >> $@ cat fpoptimizer/fpoptimizer_footer.txt >> $@ util/tree_grammar_parser: \ util/tree_grammar_parser.o \ fpoptimizer/opcodename.o $(LD) -o $@ $^ $(LDFLAGS) util/tree_grammar_parser.cc: \ util/tree_grammar_parser.y bison --output=$@ $< sed -i 's/ *$$//' $@ util/cpp_compress: \ util/cpp_compress.o util/cpp_compress_main.o $(LD) -o $@ $^ $(LDFLAGS) util/bytecoderules_parser: util/bytecoderules_parser.o $(LD) -o $@ $^ $(LDFLAGS) util/version_changer: util/version_changer.cc g++ -O3 $^ -s -o $@ $(LDFLAGS) $(CXXFLAGS) $(CPPFLAGS) util/make_function_name_parser: util/make_function_name_parser.cc util/cpp_compress.o g++ -O3 $^ -s -o $@ $(LDFLAGS) $(CXXFLAGS) $(CPPFLAGS) util/powi_opt: \ util/powi_opt.o \ fpoptimizer/hash.o \ fpoptimizer/constantfolding.o \ fpoptimizer/codetree.o \ fpoptimizer/valuerange.o \ fpoptimizer/rangeestimation.o g++ -O3 $^ -s -o $@ $(LDFLAGS) $(CXXFLAGS) $(CPPFLAGS) util/create_testrules_for_optimization_rules: \ util/create_testrules_for_optimization_rules.cc \ fpoptimizer/grammar_data.o \ fpoptimizer/opcodename.o \ fpoptimizer/grammar.o g++ -O3 $^ -s -o $@ $(LDFLAGS) $(CXXFLAGS) $(CPPFLAGS) fpoptimizer_tests.sh: util/create_testrules_for_optimization_rules ./$< > $@ chmod +x $@ set_version_string: util/version_changer util/version_changer $(RELEASE_VERSION) fparser.cc \ fparser.hh fparser_mpfr.hh fparser_gmpint.hh fpconfig.hh \ fpoptimizer.cc extrasrc/fptypes.hh extrasrc/fpaux.hh \ extrasrc/fp_opcode_add.inc \ fpoptimizer/fpoptimizer_header.txt \ util/bytecoderules_header.txt \ docs/fparser.html webpage/index.html pack: set_version_string distro_pack devel_pack distro_pack: $(RELEASE_PACK_FILES) zip -9 fparser$(RELEASE_VERSION).zip $(RELEASE_PACK_FILES) # Use KZIP&ZIPMIX (advsys.net/ken), if possible, to create a smaller zip file if which kzip; then \ rm -rf fparser-$(RELEASE_VERSION);\ mkdir fparser-$(RELEASE_VERSION); \ tar cf - $(RELEASE_PACK_FILES) | tar -x -v -C fparser-$(RELEASE_VERSION) -f -; \ for s in -b0 -b128 -b256 -b512 -b1024 \ -rn -rn -rn -rn -rn -rn -rn -rn \ -rn -rn -rn -rn -rn -rn -rn -rn; do \ (cd fparser-$(RELEASE_VERSION); \ kzip -r -y "$$s" ../fparser$(RELEASE_VERSION)-tmp.zip * );\ DeflOpt ../fparser$(RELEASE_VERSION)-tmp.zip; \ zipmix -y fparser$(RELEASE_VERSION).zip \ fparser$(RELEASE_VERSION)-tmp.zip \ fparser$(RELEASE_VERSION)-tmp2.zip; \ if [ -f fparser$(RELEASE_VERSION)-tmp2.zip ]; then \ mv -f fparser$(RELEASE_VERSION)-tmp2.zip fparser$(RELEASE_VERSION).zip; \ fi; \ ls -al fparser$(RELEASE_VERSION)*.zip; \ done; \ rm -f fparser$(RELEASE_VERSION)-tmp.zip; \ fi devel_pack: tar --exclude='*~' \ --transform="s|^|fparser_$(RELEASE_VERSION)_devel/|" \ -cjvf fparser$(RELEASE_VERSION)_devel.tar.bz2 \ Makefile examples/example.cc examples/example2.cc fparser.cc \ fparser.hh fparser_mpfr.hh fparser_gmpint.hh \ fpconfig.hh extrasrc/fptypes.hh extrasrc/fpaux.hh \ extrasrc/fp_opcode_add.inc \ extrasrc/fp_identifier_parser.inc \ testbed_tests.inc \ util/speedtest.cc testbed.cc \ tests/*.cc tests/*.txt tests/*/* \ util/*.cc util/*.hh util/*.dat util/*.txt util/*.y \ docs/fparser.html docs/style.css docs/lgpl.txt docs/gpl.txt \ fpoptimizer/*.hh fpoptimizer/*.cc \ fpoptimizer/*.dat \ fpoptimizer/*.txt \ lib/*.hh \ mpfr/MpfrFloat.hh mpfr/MpfrFloat.cc \ mpfr/GmpInt.hh mpfr/GmpInt.cc \ run_full_release_testing.sh \ util/functioninfo.cc clean: rm -f testbed testbed_release \ speedtest speedtest_release \ functioninfo \ examples/example examples/example2 ftest powi_speedtest \ util/tree_grammar_parser \ tests/make_tests \ util/bytecoderules_parser \ util/cpp_compress \ util/make_function_name_parser \ examples/*.o \ fpoptimizer/*.o \ tests/*.o \ mpfr/*.o \ util/*.o \ *.o \ .dep \ util/tree_grammar_parser.output release_clean: rm -f testbed_release speedtest_release \ testbed.o fparser.o fpoptimizer.o distclean: clean rm -f *~ TESTBED_TEST_FILES = $(wildcard tests/*/*) testbed_tests.inc: $(TESTBED_TEST_FILES) .dep: echo -n '' > .dep - g++ -MM -MG $(CPPFLAGS) $(wildcard *.cc) >> .dep - g++ -MM $(CPPFLAGS) $(wildcard examples/*.cc) | sed 's|^.*.o:|examples/&|' >> .dep - g++ -MM $(CPPFLAGS) $(wildcard fpoptimizer/*.cc) | sed 's|^.*.o:|fpoptimizer/&|' >> .dep - g++ -MM $(CPPFLAGS) $(wildcard tests/*.cc) | sed 's|^.*.o:|tests/&|' >> .dep - g++ -MM $(CPPFLAGS) $(wildcard util/*.cc) | sed 's|^.*.o:|util/&|' >> .dep - g++ -MM $(CPPFLAGS) $(wildcard mpfr/*.cc) | sed 's|^.*.o:|mpfr/&|' >> .dep - g++ -MM $(CPPFLAGS) $(wildcard lib/*.cc) | sed 's|^.*.o:|lib/&|' >> .dep sed -i "s@`pwd`/@@" .dep -include .dep fparserc++-4.5.2/docs/000077500000000000000000000000001332731714600144145ustar00rootroot00000000000000fparserc++-4.5.2/docs/fparser.html000066400000000000000000002224441332731714600167540ustar00rootroot00000000000000 Function Parser for C++ v4.5.2 : Documentation

Function Parser for C++ v4.5.2

Authors: Juha Nieminen (http://iki.fi/warp/), Joel Yliluoma (http://iki.fi/bisqwit/).

The usage license of this library is located at the end of this file.

Table of contents:

What's new

What's new in v4.5.2

Preface

This C++ library offers a class which can be used to parse and evaluate a mathematical function from a string (which might be eg. requested from the user). The syntax of the function string is similar to mathematical expressions written in C/C++ (the exact syntax is specified later in this document). The function can then be evaluated with different values of variables.

For example, a function like "sin(sqrt(x*x+y*y))" can be parsed from a string (either std::string or a C-style string) and then evaluated with different values of x and y. This library can be useful for evaluating user-inputted functions, or in some cases interpreting mathematical expressions in a scripting language.

This library aims for maximum speed in both parsing and evaluation, while keeping maximum portability. The library should compile and work with any standard-conforming C++ compiler.

Different numerical types are supported: double, float, long double, long int, std::complex (of types double, float and long double), multiple-precision floating point numbers using the MPFR library, and arbitrary precision integers using the GMP library. (Note that it's not necessary for these two libraries to exist in the system in order to use the Function Parser library with the other numerical types. Support for these libraries is optionally compiled in using preprocessor settings.)

Usage

To use the FunctionParser class, you have to include "fparser.hh" in your source code files which use the FunctionParser class.

If you are going to use the MPFR version of the library, you need to include "fparser_mpfr.hh". If you are going to use the GMP version of the library, you need to include "fparser_gmpint.hh". (Note that support for these special parser versions needs to be specified with preprocessor macros. See the documentation below for details.)

When compiling, you have to compile fparser.cc and fpoptimizer.cc and link them to the main program. In many developement environments it's enough to add those two files to your current project (usually header files don't have to be added to the project for the compilation to work).

If you are going to use the MPFR or the GMP versions of the library, you also need to add mpfr/MpfrFloat.cc or mpfr/GmpInt.cc files to your project, respectively. Otherwise they should not be added to the project.

Note that part of the library source code is inside several .inc files inside the extrasrc subdirectory (these files contain auto-generated C++ code), provided in the library package. These files are used by fparser.cc and don't need to be added explicitly to the project in most IDEs (such as Visual Studio). Basically, you don't need to do anything with these files, other than keep them in the extrasrc subdirectory.

Simple usage example of the library:

    FunctionParser fp;
    fp.Parse("sqrt(x*x + y*y)", "x,y");
    double variables[2] = { 1.5, 2.9 };
    double result = fp.Eval(variables);

Parser types

Different versions of the function parser class are supported, using different floating point or integral types for function evaluation.

All the classes other than the default one, FunctionParser, need to be enabled at compile time by defining a preprocessor macro (specified below) either in the fpconfig.hh file or your compiler settings. (The reason for this is that every parser that is included in the compilation process will make the compilation slower and increase the size of the executable, so they are compiled only on demand. Also, the GMP and MPFR versions of the parser require for those libraries to be available, which is often not the case.)

Note that if you try to use the other class types without enabling them with the correspondent preprocessor macro, you will get a linker error (rather than a compiler error) because those classes will not have been instantiated when the library was compiled.

Currently the Optimize() method works only for the FunctionParser, FunctionParser_f and FunctionParser_ld classes. For the other types it can be called but it does nothing.

FunctionParser

This is the default class, which uses double as its numerical type. This is the only class enabled by default.

If you use some other type than this one, and you don't want this version of the class compiled into the library, you can define the preprocessor macro FP_DISABLE_DOUBLE_TYPE.

FunctionParser_f

This parser uses float as its numerical type.

The FP_SUPPORT_FLOAT_TYPE preprocessor macro needs to be defined for this class to be enabled.

FunctionParser_ld

This parser uses long double as its numerical type.

The FP_SUPPORT_LONG_DOUBLE_TYPE preprocessor macro needs to be defined for this class to be enabled.

Note that the FP_USE_STRTOLD preprocessor macro should also be defined when using this version of the parser if the compiler supports the (C99) function strtold(). (See documentation below.)

FunctionParser_li

This parser uses long int as its numerical type.

The FP_SUPPORT_LONG_INT_TYPE preprocessor macro needs to be defined for this class to be enabled.

Note that this version of the class uses a reduced function syntax with support only for functions which are feasible to be used with integral types (namely abs(), eval(), if(), min() and max(), besides basic arithmetic operators, except for the power operator).

FunctionParser_cd, FunctionParser_cf, FunctionParser_cld

These parsers use std::complex<double>, std::complex<float> and std::complex<long double> as their numerical type, respectively.

The preprocessor macros to enable them are FP_SUPPORT_COMPLEX_DOUBLE_TYPE, FP_SUPPORT_COMPLEX_FLOAT_TYPE and FP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE.

If FunctionParser_cld is used, the FP_USE_STRTOLD macro should also be defined if the compiler supports the strtold() function.

FunctionParser_mpfr

This parser uses MpfrFloat as its numerical type.

The FP_SUPPORT_MPFR_FLOAT_TYPE preprocessor macro needs to be defined for this class to be enabled.

Note that to use this version of the parser, "fparser_mpfr.hh" needs to be included.

MpfrFloat is an auxiliary class which uses the MPFR library for multiple-precision floating point numbers. The class behaves largely like a floating point type, and is declared in the mpfr/MpfrFloat.hh file (see that file for info about the public interface of the class).

If this class is enabled, mpfr/MpfrFloat.cc needs to be compiled into the project, as well as the GMP and MPFR libraries. (With the gcc compiler this means using the linker options "-lgmp -lmpfr".)

FunctionParser_gmpint

This parser uses GmpInt as its numerical type.

The FP_SUPPORT_GMP_INT_TYPE preprocessor macro needs to be defined for this class to be enabled.

Note that to use this version of the parser, "fparser_gmpint.hh" needs to be included.

GmpInt is an auxiliary class which uses the GMP library for arbitrary-precision integer numbers. The class behaves largely like an integer type, and is declared in the mpfr/GmpInt.hh file (see that file for info about the public interface of the class).

If this class is enabled, mpfr/GmpInt.cc needs to be compiled into the project, as well as the GMP library.

This version of the class also uses a reduced version of the syntax, like the long int version.

Note: Since there's no upper limit to the size of GMP integers, this version of the class should be used with care in situations where malicious users might be able to exploit it to make the program run out of memory. An example of this would be a server-side application usable through the WWW.

Note that these different classes are completely independent and instances of different classes cannot be given to each other using the AddFunction() method. Only objects of the same type can be given to that method.

The rest of the documentation assumes that FunctionParser (which uses the double type) is used. The usage of the other classes is identical except that double is replaced with the correspondent type used by that class. (In other words, whenever the rest of this documentation uses the type keyword 'double', the correspondent type should be used instead, when using another version of the class.)

Configuring the compilation

There is a set of precompiler options in the fpconfig.hh file which can be used for setting certain features on or off. All of these options can also be specified from the outside, using precompiler settings (eg. the -D option in gcc), and thus it's not necessary to modify this file.

FP_USE_STRTOLD : (Default off)

If FunctionParser_ld or FunctionParser_cld are used, this preprocessor macro should be defined if the compiler supports the (C99) function strtold(). If not, then numeric literals will be parsed with double precision only, which in most systems is less accurate than long double precision, which will cause small rounding errors. (This setting has no effect on the other parser types.) Note that strtold() will also be automatically used if __cplusplus indicates that C++11 is in use.

FP_SUPPORT_CPLUSPLUS11_MATH_FUNCS : (Default off)

Use C++11 math functions where applicable. (These are ostensibly faster than the equivalent formulas using C++98 math functions.) Note that not all compilers support these functions (even if they otherwise support C++11.)

FP_SUPPORT_OPTIMIZER : (Default on)

If you are not going to use the Optimize() method, you can comment this line out to speed-up the compilation a bit, as well as making the binary a bit smaller. (Optimize() can still be called, but it will not do anything.)

You can also disable the optimizer by specifying the FP_NO_SUPPORT_OPTIMIZER precompiler constant in your compiler settings.

FP_USE_THREAD_SAFE_EVAL : (Default off)

Define this precompiler constant to make Eval() thread-safe. Refer to the thread safety section later in this document for more information. Note that defining this may make Eval() slightly slower.

Also note that the MPFR and GMP versions of the library cannot be made thread-safe, and thus this setting has no effect on them.

FP_USE_THREAD_SAFE_EVAL_WITH_ALLOCA : (Default off)

This is like the previous, but makes Eval() use the alloca() function (instead of std::vector). This will make it faster, but the alloca() function is not standard and thus not supported by all compilers.

Copying and assignment

The class implements a safe copy constructor and assignment operator.

It uses the copy-on-write technique for efficiency. This means that when copying or assigning a FunctionParser instance, the internal data (which in some cases can be quite lengthy) is not immediately copied but only when the contents of the copy (or the original) are changed.

This means that copying/assigning is a very fast operation, and if the copies are never modified then actual data copying never happens either.

The Eval() and EvalError() methods of the copy can be called without the internal data being copied.

Calling Parse(), Optimize() or the user-defined constant/function adding methods will cause a deep-copy.

Short descriptions of FunctionParser methods

int Parse(const std::string& Function, const std::string& Vars,
          bool useDegrees = false);

int Parse(const char* Function, const std::string& Vars,
          bool useDegrees = false);

Parses the given function and compiles it to internal format. Return value is -1 if successful, else the index value to the location of the error.


void setDelimiterChar(char);

Sets an ending delimiter character for the function string. (See the long description for more details.)


static double epsilon();
static void setEpsilon(double);

Setter and getter for the epsilon value used with comparison operators.


const char* ErrorMsg(void) const;

Returns an error message corresponding to the error in Parse(), or an empty string if no such error occurred.


ParseErrorType GetParseErrorType() const;

Returns the type of parsing error which occurred. Possible return types are described in the long description.


double Eval(const double* Vars);

Evaluates the function given to Parse().


int EvalError(void) const;

Returns 0 if no error happened in the previous call to Eval(), else an error code >0.


void Optimize();

Tries to optimize the bytecode for faster evaluation.


bool AddConstant(const std::string& name, double value);

Add a constant to the parser. Returns false if the name of the constant is invalid, else true.


bool AddUnit(const std::string& name, double value);

Add a new unit to the parser. Returns false if the name of the unit is invalid, else true.


bool AddFunction(const std::string& name,
                 double (*functionPtr)(const double*),
                 unsigned paramsAmount);

Add a user-defined function to the parser (as a function pointer). Returns false if the name of the function is invalid, else true.


bool AddFunction(const std::string& name, FunctionParser&);

Add a user-defined function to the parser (as a FunctionParser instance). Returns false if the name of the function is invalid, else true.


bool RemoveIdentifier(const std::string& name);

Removes the constant, unit or user-defined function with the specified name from the parser.


int ParseAndDeduceVariables(const std::string& function,
                            int* amountOfVariablesFound = 0,
                            bool useDegrees = false);
int ParseAndDeduceVariables(const std::string& function,
                            std::string& resultVarString,
                            int* amountOfVariablesFound = 0,
                            bool useDegrees = false);
int ParseAndDeduceVariables(const std::string& function,
                            std::vector<std::string>& resultVars,
                            bool useDegrees = false);

Like Parse(), but the variables in the function are deduced automatically. The amount of found variables and the variable names themselves are returned by the different versions of the function.

Long descriptions of FunctionParser methods


int Parse(const std::string& Function, const std::string& Vars,
          bool useDegrees = false);

int Parse(const char* Function, const std::string& Vars,
          bool useDegrees = false);

Parses the given function (and compiles it to internal format). Destroys previous function. Following calls to Eval() will evaluate the given function.

The strings given as parameters are not needed anymore after parsing.

Parameters:
Function String containing the function to parse.
Vars String containing the variable names, separated by commas.
Eg. "x,y", "VarX,VarY,VarZ,n" or "x1,x2,x3,x4,__VAR__".
useDegrees (Optional.) Whether to use degrees or radians in trigonometric functions. (Default: radians)

If a char* is given as the Function parameter, it must be a null-terminated string.

Variables can have any size and they are case sensitive (ie. "var", "VAR" and "Var" are different variable names). Letters, digits, underscores and UTF8-encoded characters can be used in variable names, but the name of a variable can't begin with a digit. Each variable name can appear only once in the 'Vars' string. Function names are not legal variable names.

Using longer variable names causes no overhead whatsoever to the Eval() method, so it's completely safe to use variable names of any size.

The third, optional parameter specifies whether angles should be interpreted as radians or degrees in trigonometrical functions. If not specified, the default value is radians.

Return values:

Example: parser.Parse("3*x+y", "x,y");


void setDelimiterChar(char);

By default the parser expects the entire function string to be valid (ie. the entire contents of the given std::string, or a C string ending in the null character '\0').

If a delimiter character is specified with this function, then if it's encountered at the outermost parsing level by the Parse() function, and the input function has been valid so far, Parse() will return an index to this character inside the input string, but rather than set an error code, FP_NO_ERROR will be set.

The idea is that this can be used to more easily parse functions which are embedded inside larger strings, containing surrounding data, without having to explicitly extract the function to a separate string.

For example, suppose you are writing an interpreter for a scripting language, which can have commands like this:

let MyFunction(x,y) = { sin(x*x+y*y) } // A 2-dimensional function

Normally when parsing such a line you would have to extract the part inside the curly brackets into a separate string and parse it that way. With this feature what you can do instead is to set '}' as the delimiter character and then simply give a pointer to the character which comes after the '{'. If all goes well, the Parse() function will return an index to the '}' character (from the given starting point) and GetParseErrorType() will return FP_NO_ERROR. You can use the return value (if it's not -1) to jump forward in the string to the delimiter character.

Note that a null character ('\0') or the end of the std::string (if one was given) will still be a valid end of the function string even if a delimiter character was specified. (In this case Parse() will return -1 if there was no error, as usual.)

Also note that the delimiter character cannot be any valid operator or alphanumeric (including the underscore) character, nor the other characters defined in the function syntax. It must be a character not supported by the function parser (such as '}', '"', ']', etc).


static double epsilon();
static void setEpsilon(double);

Comparison operators (for the non-integral versions of the parser) use an epsilon value to account for floating point calculation rounding errors. This epsilon value can be set and read with these functions. (Note that the specified value will be used by all instances of FunctionParser.) If not specified, the default values are:


const char* ErrorMsg(void) const;

Returns a pointer to an error message string corresponding to the error caused by Parse() (you can use this to print the proper error message to the user). If no such error has occurred, returns an empty string.


ParseErrorType GetParseErrorType() const;

Returns the type of parse error which occurred.

This method can be used to get the error type if ErrorMsg() is not enough for printing the error message. In other words, this can be used for printing customized error messages (eg. in another language). If the default error messages suffice, then this method doesn't need to be called. FunctionParser::ParseErrorType is an enumerated type inside the class (ie. its values are accessed like "FunctionParser::SYNTAX_ERROR").

The possible values for FunctionParser::ParseErrorType are listed below, along with their equivalent error message returned by the ErrorMsg() method:

FP_NO_ERROR If no error occurred in the previous call to Parse().
SYNTAX_ERROR "Syntax error"
MISM_PARENTH "Mismatched parenthesis"
MISSING_PARENTH "Missing ')'"
EMPTY_PARENTH "Empty parentheses"
EXPECT_OPERATOR "Syntax error: Operator expected"
OUT_OF_MEMORY "Not enough memory"
UNEXPECTED_ERROR "An unexpected error occurred. Please make a full bug report to the author"
INVALID_VARS "Syntax error in parameter 'Vars' given to FunctionParser::Parse()"
ILL_PARAMS_AMOUNT "Illegal number of parameters to function"
PREMATURE_EOS "Syntax error: Premature end of string"
EXPECT_PARENTH_FUNC "Syntax error: Expecting ( after function"
UNKNOWN_IDENTIFIER "Syntax error: Unknown identifier"
NO_FUNCTION_PARSED_YET "(No function has been parsed yet)"


double Eval(const double* Vars);

Evaluates the function given to Parse(). The array given as parameter must contain the same amount of values as the amount of variables given to Parse(). Each value corresponds to each variable, in the same order.

Return values:

Example:

double Vars[] = {1, -2.5};
double result = parser.Eval(Vars);


int EvalError(void) const;

Used to test if the call to Eval() succeeded.

Return values:

If there was no error in the previous call to Eval(), returns 0, else returns a positive value as follows:


void Optimize();

This method can be called after calling the Parse() method. It will try to simplify the internal bytecode so that it will evaluate faster (it tries to reduce the amount of opcodes in the bytecode).

For example, the bytecode for the function "5+x*y-25*4/8" will be reduced to a bytecode equivalent to the function "x*y-7.5" (the original 11 opcodes will be reduced to 5). Besides calculating constant expressions (like in the example), it also performs other types of simplifications with variable and function expressions.

This method is quite slow and the decision of whether to use it or not should depend on the type of application. If a function is parsed once and evaluated millions of times, then calling Optimize() may speed-up noticeably. However, if there are tons of functions to parse and each one is evaluated once or just a few times, then calling Optimize() will only slow down the program.

Also, if the original function is expected to be optimal, then calling Optimize() would be useless.

Note: Currently this method does not make any checks (like Eval() does) and thus things like "1/0" will cause undefined behaviour. (On the other hand, if such expression is given to the parser, Eval() will always give an error code, no matter what the parameters.) If caching this type of errors is important, a work-around is to call Eval() once before calling Optimize() and checking EvalError().

If the destination application is not going to use this method, the compiler constant FP_SUPPORT_OPTIMIZER can be undefined in fpconfig.hh to make the library smaller (Optimize() can still be called, but it will not do anything).

(If you are interested in seeing how this method optimizes the opcode, you can call the PrintByteCode() method before and after the call to Optimize() to see the difference.)


bool AddConstant(const std::string& name, double value);

This method can be used to add constants to the parser. Syntactically constants are identical to variables (ie. they follow the same naming rules and they can be used in the function string in the same way as variables), but internally constants are directly replaced with their value at parse time.

Constants used by a function must be added before calling Parse() for that function. Constants are preserved between Parse() calls in the current FunctionParser instance, so they don't need to be added but once. (If you use the same constant in several instances of FunctionParser, you will need to add it to all the instances separately.)

Constants can be added at any time and the value of old constants can be changed, but new additions and changes will only have effect the next time Parse() is called. (That is, changing the value of a constant after calling Parse() and before calling Eval() will have no effect.)

The return value will be false if the 'name' of the constant was illegal, else true. If the name was illegal, the method does nothing.

Example: parser.AddConstant("pi", 3.1415926535897932);

Now for example parser.Parse("x*pi", "x"); will be identical to the call parser.Parse("x*3.1415926535897932", "x");


bool AddUnit(const std::string& name, double value);

In some applications it is desirable to have units of measurement. A typical example is an application which creates a page layout to be printed. When printing, distances are usually measured in points (defined by the resolution of the printer). However, it is often more useful for the user to be able to specify measurements in other units such as centimeters or inches.

A unit is simply a value by which the preceding element is multiplied. For example, if the printing has been set up to 300 DPI, one inch is then 300 points (dots). Thus saying eg. "5in" is the same as saying "5*300" or "1500" (assuming "in" has been added as a unit with the value 300).

Note that units are slightly different from a multiplication in that they have a higher precedence than any other operator (except parentheses). Thus for example "5/2in" is parsed as "5/(2*300)". (If 5/2 inches is what one wants, it has to be written "(5/2)in".)

You can use the AddUnit() method to add a new unit. The unit can then be used after any element in the function (and will work as a multiplier for that element). An element is a float literal, a constant, a variable, a function or any expression in parentheses. When the element is not a float literal nor an expression in parentheses, there has to naturally be at least one whitespace between the element and the unit (eg. "x in"). To change the value of a unit, call AddUnit() again with the same unit name and the new value.

Unit names share the same namespace as constants, functions and variables, and thus should be distinct from those.

Example: parser.AddUnit("in", 300);

Now for example the function "5in" will be identical to "(5*300)". Other usage examples include "x in", "3in+2", "pow(x,2)in", "(x+2)in".


bool AddFunction(const std::string& name,
                 double (*functionPtr)(const double*),
                 unsigned paramsAmount);
This method can be used to add new functions to the parser. For example, if you would like to add a function "sqr(A)" which squares the value of A, you can do it with this method (so that you don't need to touch the source code of the parser).

The method takes three parameters:

The return value will be false if the given name was invalid (either it did not follow the variable naming conventions, or the name was already reserved), else true. If the return value is false, nothing is added.

Example: Suppose we have a C++ function like this:

double Square(const double* p)
{
    return p[0]*p[0];
}

Now we can add this function to the parser like this:

parser.AddFunction("sqr", Square, 1);
parser.Parse("2*sqr(x)", "x");

An example of a useful function taking no parameters is a function returning a random value. For example:

double Rand(const double*)
{
    return drand48();
}

parser.AddFunction("rand", Rand, 0);

Important note: If you use the Optimize() method, it will assume that the user-given function has no side-effects, that is, it always returns the same value for the same parameters. The optimizer will optimize the function call away in some cases, making this assumption. (The Rand() function given as example above is one such problematic case.)


bool AddFunction(const std::string& name, FunctionParser&);

This method is almost identical to the previous AddFunction(), but instead of taking a C++ function, it takes another FunctionParser instance.

There are some important restrictions on making a FunctionParser instance call another:

Example:

FunctionParser f1, f2;

f1.Parse("x*x", "x");

f2.AddFunction("sqr", f1);

This version of the AddFunction() method can be useful to eg. chain user-given functions. For example, ask the user for a function F1, and then ask the user another function F2, but now the user can call F1 in this second function if he wants (and so on with a third function F3, where he can call F1 and F2, etc).


template<typename DerivedWrapper>
bool AddFunctionWrapper(const std::string& name, const DerivedWrapper&,
                        unsigned paramsAmount);

See section on specialized function objects.


bool RemoveIdentifier(const std::string& name);

If a constant, unit or user-defined function with the specified name exists in the parser, it will be removed and the return value will be true, else nothing will be done and the return value will be false.

(Note: If you want to remove everything from an existing FunctionParser instance, simply assign a fresh instance to it, ie. like "parser = FunctionParser();")


int ParseAndDeduceVariables(const std::string& function,
                            int* amountOfVariablesFound = 0,
                            bool useDegrees = false);
int ParseAndDeduceVariables(const std::string& function,
                            std::string& resultVarString,
                            int* amountOfVariablesFound = 0,
                            bool useDegrees = false);
int ParseAndDeduceVariables(const std::string& function,
                            std::vector<std::string>& resultVars,
                            bool useDegrees = false);

These functions work in the same way as the Parse() function, but the variables in the input function string are deduced automatically. The parameters are:

As with Parse(), the return value will be -1 if the parsing succeeded, else an index to the location of the error. None of the specified return values will be modified in case of error.

Specialized function objects

The AddFunction() method can be used to add a new user-defined function to the parser, its implementation being called through a C++ function pointer. Sometimes this might not be enough, though. For example, one might want to use boost::function or other similar specialized stateful function objects instead of raw function pointers. This library provides a mechanism to achieve this.

Creating and adding a specialized function object

In order to create a specialized function object, create a class derived from the FunctionParser::FunctionWrapper class. This class declares a virtual function named callFunction that the derived class must implement. For example:

class MyFunctionWrapper:
    public FunctionParser::FunctionWrapper
{
 public:
    virtual double callFunction(const double* values)
    {
        // Perform the actual function call here, like:
        return someFunctionSomewhere(values);

        // In principle the result could also be
        // calculated here, like for example:
        return values[0] * values[0];
    }
};

You can then add an instance of this class to FunctionParser using the AddFunctionWrapper() method, which works like AddFunction(), but takes a wrapper object instead of a function pointer as parameter. For example:

MyFunctionWrapper wrapper;
parser.AddFunctionWrapper("funcName", wrapper, 1);

Note that FunctionParser will internally create a copy of the wrapper object, managing the lifetime of this copy, and thus the object given as parameter does not need to exist for as long as the FunctionParser instance. Hence the above could also be written as:

parser.AddFunctionWrapper("funcName", MyFunctionWrapper(), 1);

Note that this also means that the wrapper class must have a working copy constructor.

Also note that if the FunctionParser instance is copied, all the copies will share the same function wrapper objects given to the original.

Retrieving specialized function objects

As noted, the library will internally make a copy of the wrapper object, and thus it will be separate from the one which was given as parameter to AddFunctionWrapper(). In some cases it may be necessary to retrieve this wrapper object (for example to read or change its state). This can be done with the GetFunctionWrapper() method, which takes the name of the function and returns a pointer to the wrapper object, or null if no such object exists with that name.

Note that the returned pointer will be of type FunctionParser::FunctionWrapper. In order to get a pointer to the actual derived type, the calling code should perform a dynamic_cast, for example like this:

MyFunctionWrapper* wrapper =
    dynamic_cast<MyFunctionWrapper*>
    (parser.GetFunctionWrapper("funcName"));

if(!wrapper) { /* oops, the retrieval failed */ }
else ...

(Using dynamic cast rather than a static cast adds safety because if you accidentally try to downcast to the wrong type, the pointer will become null.)

The calling code is free to modify the object in any way it wants, but it must not delete it (because FunctionParser itself handles this).

FunctionParserBase

All the different parser types are derived from a templated base class named FunctionParserBase. In normal use it's not necessary to directly refer to this base class in the calling code. However, if the calling code also needs to be templated (with respect to the numerical type), then using FunctionParserBase directly is the easiest way to achieve this.

For example, if you want to make a function that handles more than one type of parser, it can be done like this:

template<typename Value_t>
void someFunction(FunctionParserBase<Value_t>& parser)
{
    // do something with 'parser' here
}

Now it's convenient to call that function with more than one type of parser, for example:

FunctionParser realParser;
FunctionParser_cd complexParser;

someFunction(realParser);
someFunction(complexParser);

Another example is a class that inherits from FunctionParser which also wants to support different numerical types. Such class can be declared as:

template<typename Value_t>
class SpecializedParser: public FunctionParserBase<Value_t>
{
    ...
};

Syntax

Numeric literals

A numeric literal is a fixed numerical value in the input function string (either a floating point value or an integer value, depending on the parser type).

An integer literal can consist solely of numerical digits (possibly with a preceding unary minus). For example, "12345".

If the literal is preceded by the characters "0x", it will be interpreted as a hexadecimal literal, where digits can also include the letters from 'A' to 'F' (in either uppercase or lowercase). For example, "0x89ABC" (which corresponds to the value 563900).

A floating point literal (only supported by the floating point type parsers) may additionally include a decimal point followed by the decimal part of the value, such as for example "12.34", optionally followed by a decimal exponent.

A decimal exponent consists of an 'E' or 'e', followed by an optional plus or minus sign, followed by decimal digits, and indicates multiplication by a power of 10. For example, "1.2e5" (which is equivalent to the value 120000).

If a floating point literal is preceded by the characters "0x" it will be interpreted in hexadecimal. A hexadecimal floating point literal consists of a hexadecimal value, with an optional decimal point, followed optionally by a binary exponent in base 10 (in other words, the exponent is not in hexadecimal).

A binary exponent has the same format as a decimal exponent, except that 'P' or 'p' is used. A binary exponent indicates multiplication by a power of 2. For example, "0xA.Bp10" (which is equivalent to the value 10944).

With the complex versions of the library, the imaginary part of a numeric literal is written as a regular numeric literal with an 'i' appended, for example "5i". Note that when also specifying the real part of a complex literal, parentheses should be used to avoid precedence problems. (For example, "(2+5i) * x" is not the same thing as "2+5i * x". The latter would be equivalent to "2 + (5i * x)".)

Identifier names

An identifier is the name of a function (internal or user-defined), variable, constant or unit. New identifiers can be specified with the functions described in the earlier subsections in this document.

The name of an identifier can use any alphanumeric characters, the underscore character and any UTF8-encoded unicode character, excluding those denoting whitespace. The first character of the name cannot be a numeric digit, though.

All functions, variables, constants and units must use unique names. It's not possible to add two different identifiers with the same name.

The function string syntax

The function string understood by the class is very similar (but not completely identical in all aspects) to mathematical expressions in the C/C++ languages. Arithmetic float expressions can be created from float literals, variables or functions using the following operators in this order of precedence:

() expressions in parentheses first
A unit a unit multiplier (if one has been added)
A^B exponentiation (A raised to the power B)
-A unary minus
!A unary logical not (result is 1 if int(A) is 0, else 0)
A*B A/B A%B multiplication, division and modulo
A+B A-B addition and subtraction
A=B A<B A<=B
A!=B A>B A>=B
comparison between A and B (result is either 0 or 1)
A&B result is 1 if int(A) and int(B) differ from 0, else 0.
Note: Regardless of the values, both operands are always evaluated. However, if the expression is optimized, it may be changed such that only one of the operands is evaluated, according to standard shortcut logical operation semantics.
A|B result is 1 if int(A) or int(B) differ from 0, else 0.
Note: Regardless of the values, both operands are always evaluated. However, if the expression is optimized, it may be changed such that only one of the operands is evaluated, according to standard shortcut logical operation semantics.

(Note that currently the exponentiation operator is not supported for FunctionParser_li nor FunctionParser_gmpint. With the former the result would very easily overflow, making its usefulness questionable. With the latter it could be easily abused to make the program run out of memory; think of a function like "10^10^10^100000".)

Since the unary minus has higher precedence than any other operator, for example the following expression is valid: x*-y

The comparison operators use an epsilon value, so expressions which may differ in very least-significant digits should work correctly. For example, "0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1 = 1" should always return 1, and the same comparison done with ">" or "<" should always return 0. (The epsilon value can be configured in the fpconfig.hh file.) Without epsilon this comparison probably returns the wrong value.

The class supports these functions:

abs(A) Absolute value (magnitude) of A. With real numbers, if A is negative, returns -A otherwise returns A. With complex numbers, equivalent to hypot(real(x),imag(x)).
acos(A) Arc-cosine of A. Returns the angle, measured in radians, whose cosine is A.
acosh(A) Same as acos() but for hyperbolic cosine.
arg(A) Phase angle of complex number A. Equivalent to atan2(imag(x),real(x)).
asin(A) Arc-sine of A. Returns the angle, measured in radians, whose sine is A.
asinh(A) Same as asin() but for hyperbolic sine.
atan(A) Arc-tangent of (A). Returns the angle, measured in radians, whose tangent is A.
atan2(A,B) Principal arc-tangent of A/B, using the signs of the two arguments to determine the quadrant of the result. Returns the solution to the two expressions hypot(A,B)*sin(x)=A, hypot(A,B)*cos(x)=B. The return value is in range -pi to pi, inclusive.
atanh(A) Same as atan() but for hyperbolic tangent.
cbrt(A) Cube root of A. Returns a solution to expression pow(x,3)=A.
conj(A) Complex conjugate of A. Equivalent to real(x) - 1i*imag(x) or polar(abs(x),-arg(x)).
ceil(A) Ceiling of A. Returns the smallest integer not smaller than A. Rounds up to the next higher integer. E.g. -2.9, -2.5 and -2.1 are rounded to -2.0, and 2.9, 2.5 and 2.1 are rounded to 3.0.
cos(A) Cosine of A. Returns the cosine of the angle A, where A is measured in radians.
cosh(A) Same as cos() but for hyperbolic cosine.
cot(A) Cotangent of A. Equivalent to 1/tan(A).
csc(A) Cosecant of A. Equivalent to 1/sin(A).
eval(...) This a recursive call to the function to be evaluated. The number of parameters must be the same as the number of parameters taken by the function. Must be called inside if() to avoid infinite recursion.
exp(A) Exponential of A. Returns the value of e raised to the power A where e is the base of the natural logarithm, i.e. the non-repeating value approximately equal to 2.71828182846.
exp2(A) Base 2 exponential of A. Equivalent to pow(2,A).
floor(A) Floor of A. Returns the largest integer not greater than A. Rounds down to the next lower integer. E.g. -2.9, -2.5 and -2.1 are rounded to -3.0, and 2.9, 2.5 and 2.1 are rounded to 2.0.
hypot(A,B) Euclidean distance function. Equivalent to sqrt(A^2+B^2).
if(A,B,C) If int(A) differs from 0, the return value of this function is B, else C. Only the parameter which needs to be evaluated is evaluated, the other parameter is skipped; this makes it safe to use eval() in them.
imag(A) Return the imaginary part of complex number A. Equivalent to abs(A)*sin(arg(A)).
int(A) Rounds A to the closest integer. Equidistant values are rounded away from zero. E.g. -2.9 and -2.5 are rounded to -3.0; -2.1 is rounded to -2.0, and 2.9 and 2.5 are rounded to 3.0; 2.1 is rounded to 2.0.
log(A) Natural (base e) logarithm of A. Returns the solution to expression exp(x)=A.
log2(A) Base 2 logarithm of A. Equivalent to log(A)/log(2).
log10(A) Base 10 logarithm of A.
max(A,B) If A>B, the result is A, else B.
min(A,B) If A<B, the result is A, else B.
polar(A,B) Returns a complex number from magnitude A, phase angle B (in radians). Equivalent to real(A)*(cos(real(B))+1i*sin(real(B))).
pow(A,B) Exponentiation (A raised to the power B).
real(A) Return the real part of complex number A. Equivalent to abs(A)*cos(arg(A)).
sec(A) Secant of A. Equivalent to 1/cos(A).
sin(A) Sine of A. Returns the sine of the angle A, where A is measured in radians.
sinh(A) Same as sin() but for hyperbolic sine.
sqrt(A) Square root of A. Returns a solution to expression pow(x,2)=A.
tan(A) Tangent of A. Returns the tangent of the angle A, where A is measured in radians.
tanh(A) Same as tan() but for hyperbolic tangent.
trunc(A) Truncated value of A. Returns an integer corresponding to the value of A without its fractional part. E.g. -2.9, -2.5 and -2.1 are rounded to -2.0, and 2.9, 2.5 and 2.1 are rounded to 2.0.

(Note that for FunctionParser_li and FunctionParser_gmpint only the functions abs(), eval(), if(), min() and max() are supported.)

Examples of function string understood by the class:

"1+2"
"x-1"
"-sin(sqrt(x^2+y^2))"
"sqrt(XCoord*XCoord + YCoord*YCoord)"

An example of a recursive function is the factorial function: "if(n>1, n*eval(n-1), 1)"

Note that a recursive call has some overhead, which makes it a bit slower than any other operation. It may be a good idea to avoid recursive functions in very time-critical applications. Recursion also takes some memory, so extremely deep recursions should be avoided (eg. millions of nested recursive calls).

Also note that even though the maximum recursion level of eval() is limited, it is possible to write functions which never reach that level but still take enormous amounts of time to evaluate. This can sometimes be undesirable because it is prone to exploitation, which is why eval() is disabled by default. It can be enabled in the fpconfig.hh file.

Inline variables

The function syntax supports defining new variables inside the function string itself. This can be done with the following syntax:

"<variable name> := <expression>; <function>"

For example:

"length := sqrt(x*x+y*y); 2*length*sin(length)"

(Spaces around the ':=' operator are optional.)

The obvious benefit of this is that if a long expression needs to be used in the function several times, this allows writing it only once and using a named variable from that point forward.

The variable name must be an unused identifier (in other words, not an existing function, variable or unit name).

The <function> part can have further inline variable definitions, and thus it's possible to have any amount of them, for example:

"A := x^2; B := y^2; C := z^2; sqrt(A+B+C)"

The expressions in subsequent inline variable definitions can use any of the previous inline variables. It is also possible to redefine an inline variable. For example:

"A := x^2; A := 2*A; sqrt(A)"

Whitespace

Arbitrary amounts of whitespace can optionally be included between elements in the function string. The following unicode characters are interpreted as whitespace:
Character number Character name UTF-8 byte sequence
U+0009HORIZONTAL TABULATION 09
U+000ALINE FEED 0A
U+000BVERTICAL TABULATION 0B
U+000DCARRIAGE RETURN 0D
U+0020SPACE 20
U+00A0NO-BREAK SPACE C2 A0
U+2000EN QUAD E2 80 80
U+2001EM QUAD E2 80 81
U+2002EN SPACE E2 80 82
U+2003EM SPACE E2 80 83
U+2004THREE-PER-EM SPACE E2 80 84
U+2005FOUR-PER-EM SPACE E2 80 85
U+2006SIX-PER-EM SPACE E2 80 86
U+2007FIGURE SPACE E2 80 87
U+2008PUNCTUATION SPACE E2 80 88
U+2009THIN SPACE E2 80 89
U+200AHAIR SPACE E2 80 8A
U+200BZERO WIDTH SPACE E2 80 8B
U+202FNARROW NO-BREAK SPACE E2 80 AF
U+205FMEDIUM MATHEMATICAL SPACEE2 81 9F
U+3000IDEOGRAPHIC SPACE E3 80 80

Miscellaneous

About floating point accuracy

Note that if you are using FunctionParser_ld or FunctionParser_cld and you want calculations to be as accurate as the long double type allows, you should pay special attention to floating point literals in your own code. For example, this is a very typical mistake:

FunctionParser_ld parser;
parser.AddConstant("pi", 3.14159265358979323846);

The mistake might not be immediately apparent. The mistake is that a literal of type double is passed to the AddConstant() function even though it expects a value of type long double. In most systems the latter has more bits of precision than the former, which means that the value will have its least-significant bits clipped, introducing a rounding error. The proper way of making the above calls is:

FunctionParser_ld parser;
parser.AddConstant("pi", 3.14159265358979323846L);

The same principle should be used everywhere in your own code, if you are using the long double type.

This is especially important if you are using the MpfrFloat type (in which case its string-parsing constructor or its ParseValue() or parseString() member functions should be used instead of using numerical literals).

About evaluation-time checks

FunctionParser::Eval() will perform certain sanity checks before performing certain operations. For example, before calling the sqrt function, it will check if the parameter is negative, and if so, it will set the proper error code instead of calling the function. These checks include:

However, the library can not guarantee that it will catch all possible floating point errors before performing them, because this is impossible to do with standard C++. For example, dividing a very large value by a value which is very close to zero, or calculating the logarithm of a very small value may overflow the result, as well as multiplying two very large values. Raising a negative number to a non-integral power may cause a NaN result, etc.

As a rule of thumb, the library will (by default) detect invalid operations if they are invalid for a range of values. For example, square root is undefined for all negative values, and arc sine is undefined only values outside the range [-1, 1]. In general, operations which are invalid for only one single value (rather than a contiguous range of values) will not be detected (division by the exact value of zero is an exception to this rule) nor will overflow/underflow situations.

The library cannot guarantee that floating point errors will never happen during evaluation. This can make the library to return the floating point values inf and NaN. Moreover, if floating point errors cause an interrupt in the target computer architecture and/or when using certain compiler settings, this library cannot guarantee that it will never happen.

Note that the optimizer never performs any sanity checks.

About thread safety

None of the member functions of the FunctionParser class are thread-safe. Most prominently, the Eval() function is not thread-safe. (In other words, the Eval() function of a single FunctionParser instance cannot be safely called simultaneously by two threads.)

There are ways to use this library in a thread-safe way, though. If each thread uses its own FunctionParser instance, no problems will obviously happen. Note, however, that if these instances need to be a copy of a given FunctionParser instance (eg. one where the user has entered a function), a deep copy of this instance has to be performed for each thread. By default FunctionParser uses shallow-copying (copy-on-write), which means that a simple assignment of copy construction will not copy the data itself. To force a deep copy you can all the ForceDeepCopy() function on each of the instances of each thread after the assignment or copying has been done.

Another possibility is to compile the FunctionParser library so that its Eval() function will be thread-safe. (This can be done by defining the FP_USE_THREAD_SAFE_EVAL or the FP_USE_THREAD_SAFE_EVAL_WITH_ALLOCA precompiler constant.) As long as only one thread calls the other functions of FunctionParser, the other threads can safely call the Eval() of this one instance.

Note, however, that compiling the library like this can make Eval() slightly slower. (The alloca version, if supported by the compiler, will not be as slow.)

Also note that the MPFR and GMP versions of the library cannot be made thread-safe, and thus this setting has no effect on them.

Tips and tricks

Add constants automatically to all parser objects

Often the same constants (such as pi and e) and other user-defined identifiers (such as units) are always used in all the FunctionParser objects throughout the program. It would be troublesome to always have to manually add these constants every time a new parser object is created.

There is, however, a simple way to always add these user-defined identifiers to all instances. Write a class like this:

    class ParserWithConsts: public FunctionParser
    {
     public:
        ParserWithConsts()
        {
            AddConstant("pi", 3.14159265358979323846);
            AddConstant("e", 2.71828182845904523536);
        }
    };

Now instead of using FunctionParser, always use ParserWithConsts. It will behave identically except that the constants (and possibly other user-defined identifiers) will always be automatically defined. (Objects of this type even survive slicing, so they are completely safe to use anywhere.)

Contacting the author

Any comments, bug reports, etc. should be sent to warp@iki.fi

Usage license

Copyright © 2003-2011 Juha Nieminen, Joel Yliluoma

This Library is distributed under the Lesser General Public License (LGPL) version 3. fparserc++-4.5.2/docs/gpl.txt000066400000000000000000001045131332731714600157430ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . fparserc++-4.5.2/docs/lgpl.txt000066400000000000000000000167251332731714600161260ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. fparserc++-4.5.2/docs/style.css000066400000000000000000000015641332731714600162740ustar00rootroot00000000000000html { background-color: #E0E0E0; } body { background-color: white; margin-left: 7%; margin-top: 16px; margin-right: 7%; padding-top: 2em; padding-left: 7%; padding-right: 7%; padding-bottom: 2%; border-color: black; border: solid; border-width: 1px; } h1 { text-align: center; background-color: #FFEEBB; padding-bottom: 0.2em; padding-top: 0.1em; } h2 { background-color: #FFFFCC; padding-left: 0.5em; } h3 { background-color: #FFFFEE; } blockquote { padding-left: 2em; padding-right: 2em; font-style: italic; background-color: #FFFAF0; } li { padding-top: 0.3em; } pre { background-color: #E8E8E8; padding-left: 1em; padding-top: 0.5em; padding-bottom: 0.5em; } code { font-family: monospace; color: #900040; } .small { font-size: 80%; } .codecomment { color: green; } .highlight { background: #C0D0FF; } fparserc++-4.5.2/examples/000077500000000000000000000000001332731714600153025ustar00rootroot00000000000000fparserc++-4.5.2/examples/example.cc000066400000000000000000000022521332731714600172450ustar00rootroot00000000000000// Simple example file for the function parser // =========================================== /* When running the program, try for example with these values: f(x) = x^2 min x: -5 max x: 5 step: 1 */ #include "../fparser.hh" #include #include int main() { std::string function; double minx, maxx, step; FunctionParser fparser; fparser.AddConstant("pi", 3.1415926535897932); while(true) { std::cout << "f(x) = "; std::getline(std::cin, function); if(std::cin.fail()) return 0; int res = fparser.Parse(function, "x"); if(res < 0) break; std::cout << std::string(res+7, ' ') << "^\n" << fparser.ErrorMsg() << "\n\n"; } std::cout << "min x: "; std::cin >> minx; std::cout << "max x: "; std::cin >> maxx; std::cout << "step: "; std::cin >> step; if(std::cin.fail()) return 0; double vals[] = { 0 }; for(vals[0] = minx; vals[0] <= maxx; vals[0] += step) { std::cout << "f(" << vals[0] << ") = " << fparser.Eval(vals) << std::endl; } return 0; } fparserc++-4.5.2/examples/example2.cc000066400000000000000000000041401332731714600173250ustar00rootroot00000000000000// Simple example file for the function parser // =========================================== /* Note that the library has to be compiled with FP_SUPPORT_FLOAT_TYPE, FP_SUPPORT_LONG_DOUBLE_TYPE and FP_SUPPORT_LONG_INT_TYPE preprocessor macros defined for this example to work. Try with these input values with the different floating point parser types to see the difference in accuracy: f(x) = x + 1.234567890123456789 min x: 0 max x: 2 step: 1 */ #include "../fparser.hh" #include #include #include template void runExample(const char* valueTypeName) { typedef typename Parser::value_type Value_t; std::cout << "Using " << valueTypeName << " parser." << std::endl; Parser fparser; std::string function; Value_t minx, maxx, step; fparser.AddConstant("pi", Value_t(3.1415926535897932)); std::cin.ignore(); while(true) { std::cout << "f(x) = "; std::getline(std::cin, function); if(std::cin.fail()) return; int res = fparser.Parse(function, "x"); if(res < 0) break; std::cout << std::string(res+7, ' ') << "^\n" << fparser.ErrorMsg() << "\n\n"; } std::cout << "min x: "; std::cin >> minx; std::cout << "max x: "; std::cin >> maxx; std::cout << "step: "; std::cin >> step; if(std::cin.fail()) return; Value_t vals[] = { 0 }; for(vals[0] = minx; vals[0] <= maxx; vals[0] += step) { std::cout << std::setprecision(20); std::cout << "f(" << vals[0] << ") = " << fparser.Eval(vals) << std::endl; } } int main() { int choice = 0; do { std::cout << "1 = double, 2 = float, 3 = long double, 4 = long int: "; std::cin >> choice; } while(choice < 1 || choice > 4); switch(choice) { case 1: runExample("double"); break; case 2: runExample("float"); break; case 3: runExample("long double"); break; case 4: runExample("long int"); break; } return 0; } fparserc++-4.5.2/extrasrc/000077500000000000000000000000001332731714600153175ustar00rootroot00000000000000fparserc++-4.5.2/extrasrc/fp_identifier_parser.inc000066400000000000000000000203701332731714600221770ustar00rootroot00000000000000/* NOTE: Do not include this file in your project. The fparser.cc file #includes this file internally and thus you don't need to do anything (other than keep this file in the same directory as fparser.cc). Part of this file is generated code (by using the make_function_name_parser utility, found in the development version of this library). It's not intended to be modified by hand. */ unsigned nameLength = 0; const unsigned maximumNameLength = 0x80000000U-8; /* Due to the manner the identifier lengths are returned from the readOpcode() function, the maximum supported length for identifiers is 0x7FFFFFFF bytes. We minus 8 here to add some buffer, because of the multibyteness of UTF-8. Function names are limited to 0xFFFF bytes instead, but because function names that long just are not defined, the point is moot. */ const unsigned char* const uptr = (const unsigned char*) input; typedef signed char schar; while(likely(nameLength < maximumNameLength)) { unsigned char byte = uptr[nameLength+0]; /* Handle the common case of A-Za-z first */ if(byte >= 0x40) { if(byte < 0x80) // 0x40..0x7F - most common case { // Valid characters in 40..7F: A-Za-z_ // Valid bitmask for 40..5F: 01111111111111111111111111100001 // Valid bitmask for 60..7F: 01111111111111111111111111100000 if(sizeof(unsigned long) == 8) { const unsigned n = sizeof(unsigned long)*8-32; // ^ avoids compiler warning when not 64-bit unsigned long masklow6bits = 1UL << (byte & 0x3F); if(masklow6bits & ~((1UL << 0) | (0x0FUL << (0x1B )) | (1UL << n) | (0x1FUL << (0x1B+n)))) { ++nameLength; continue; } } else { unsigned masklow5bits = 1 << (byte & 0x1F); if((masklow5bits & ~(1 | (0x1F << 0x1B))) || byte == '_') { ++nameLength; continue; } } break; } if(byte < 0xF0) { if(byte < 0xE0) { if(byte < 0xC2) break; // 0x80..0xC1 if(byte == 0xC2 && uptr[nameLength+1]==0xA0) break; // skip nbsp // C2-DF - next common case when >= 0x40 // Valid sequence: C2-DF 80-BF if(schar(uptr[nameLength+1]) > schar(0xBF)) break; nameLength += 2; continue; } if(byte == 0xE0) // E0 { // Valid sequence: E0 A0-BF 80-BF if((unsigned char)(uptr[nameLength+1] - 0xA0) > (0xBF-0xA0)) break; } else { if(byte == 0xED) break; // ED is invalid // Valid sequence: E1-EC 80-BF 80-BF // And: EE-EF 80-BF 80-BF if(byte == 0xE2) { // break on various space characters if(uptr[nameLength+1] == 0x80 && (schar(uptr[nameLength+2]) <= schar(0x8B) || (uptr[nameLength+2] == 0xAF))) break; if(uptr[nameLength+1] == 0x81 && uptr[nameLength+2] == 0x9F) break; } else if(byte == 0xE3 && uptr[nameLength+1] == 0x80 && uptr[nameLength+2] == 0x80) break; // this too if(schar(uptr[nameLength+1]) > schar(0xBF)) break; } if(schar(uptr[nameLength+2]) > schar(0xBF)) break; nameLength += 3; continue; } if(byte == 0xF0) // F0 { // Valid sequence: F0 90-BF 80-BF 80-BF if((unsigned char)(uptr[nameLength+1] - 0x90) > (0xBF-0x90)) break; } else { if(byte > 0xF4) break; // F5-FF are invalid if(byte == 0xF4) // F4 { // Valid sequence: F4 80-8F if(schar(uptr[nameLength+1]) > schar(0x8F)) break; } else { // F1-F3 // Valid sequence: F1-F3 80-BF 80-BF 80-BF if(schar(uptr[nameLength+1]) > schar(0xBF)) break; } } if(schar(uptr[nameLength+2]) > schar(0xBF)) break; if(schar(uptr[nameLength+3]) > schar(0xBF)) break; nameLength += 4; continue; } if(nameLength > 0) { if(sizeof(unsigned long) == 8) { // Valid bitmask for 00..1F: 00000000000000000000000000000000 // Valid bitmask for 20..3F: 00000000000000001111111111000000 const unsigned n = sizeof(unsigned long)*8-32; // ^ avoids compiler warning when not 64-bit unsigned long masklow6bits = 1UL << byte; if(masklow6bits & (((1UL << 10)-1UL) << (16+n))) { ++nameLength; continue; } } else { if(byte >= '0' && byte <= '9') { ++nameLength; continue; } } } break; } /* This function generated with make_function_name_parser.cc */ #define lO l3 lH #define lN switch( #define lM l4 lH #define lL if('i' l5 #define lK 'n' l5 #define lJ 0x80000003U; #define lI l1 3]={ #define lH case #define lG 0x80000005U; #define lF )==0)l0( #define lE l8 3;}lH #define lD std::memcmp(uptr+ #define lC l2 3 lF #define lB lA 1]){lH #define lA :lN uptr[ #define l9 'a' lB #define l8 default:l0 #define l7 lG l0 5;}lH #define l6 <<16)| #define l5 ==uptr[ #define l4 lJ l0 3; #define l3 0x80000004U;l0 4; #define l2 lD 1,tmp, #define l1 static const char tmp[ #define l0 return lN nameLength){lH 2:lL 0]&&'f' l5 1])l0(cIf l6 0x80000002U;l0 2;lH 3 lA 0]){lH l9'b':if('s' l5 2])l0(cAbs l6 lM'r':if('g' l5 2])l0(cArg l6 l4 lE'c' lB'o' lA 2]){lH's':l0(cCos l6 lJ lH't':l0(cCot l6 lJ lE's':if('c' l5 2])l0(cCsc l6 l4 lE'e':if('x' l5 1]&&'p' l5 2])l0(cExp l6 lM'i':if(lK 1]&&'t' l5 2])l0(cInt l6 lM'l':if('o' l5 1]&&'g' l5 2])l0(cLog l6 lM'm' lB'a':if('x' l5 2])l0(cMax l6 lM'i':if(lK 2])l0(cMin l6 l4 lE'p':if('o' l5 1]&&'w' l5 2])l0(cPow l6 lM's' lB'e':if('c' l5 2])l0(cSec l6 lM'i':if(lK 2])l0(cSin l6 l4 lE't':if('a' l5 1]&&lK 2])l0(cTan l6 l4 lE 4 lA 0]){lH l9'c':if('o' l5 2]&&'s' l5 3])l0(cAcos l6 lO's':lL 2]&&lK 3])l0(cAsin l6 lO't':if('a' l5 2]&&lK 3])l0(cAtan l6 l3 l8 4;} lH'c' lB'b':if('r' l5 2]&&'t' l5 3])l0(cCbrt l6 lO'e':lL 2]&&'l' l5 3])l0(cCeil l6 lO'o' lA 2]){lH'n':if('j' l5 3])l0(cConj l6 lO's':if('h' l5 3])l0(cCosh l6 l3 l8 4;} l8 4;} lH'e':{lI'x','p','2'} ;if(lC cExp2 l6 l3} lH'i':{lI'm','a','g'} ;if(lC cImag l6 l3} lH'l':{lI'o','g','2'} ;if(lC cLog2 l6 l3} lH'r':{lI'e','a','l'} ;if(lC cReal l6 l3} lH's' lB'i':if(lK 2]&&'h' l5 3])l0(cSinh l6 lO'q':if('r' l5 2]&&'t' l5 3])l0(cSqrt l6 l3 l8 4;} lH't':{lI'a','n','h'} ;if(lC cTanh l6 l3} l8 4;} lH 5 lA 0]){lH l9'c':{lI'o','s','h'} ;if(lD 2,tmp,3 lF cAcosh l6 l7's':{lI'i','n','h'} ;if(lD 2,tmp,3 lF cAsinh l6 l7't':if('a' l5 2]){if(lK 3]){lN uptr[4]){lH'2':l0(cAtan2 l6 lG lH'h':l0(cAtanh l6 lG l8 5;} } l0 5;} l0 5;l8 5;} lH'f':{l1 4]={'l','o','o','r'} ;if(l2 4 lF cFloor l6 l7'h':{l1 4]={'y','p','o','t'} ;if(l2 4 lF cHypot l6 l7'l':{l1 4]={'o','g','1','0'} ;if(l2 4 lF cLog10 l6 l7'p':{l1 4]={'o','l','a','r'} ;if(l2 4 lF cPolar l6 l7't':{l1 4]={'r','u','n','c'} ;if(l2 4 lF cTrunc l6 lG l0 5;} l8 5;} default:break;} l0 nameLength; fparserc++-4.5.2/extrasrc/fp_opcode_add.inc000066400000000000000000002246651332731714600205770ustar00rootroot00000000000000/* Function Parser for C++ v4.5.2 NOTE: Do not include this file in your project. The fparser.cc file #includes this file internally and thus you don't need to do anything (other than keep this file in the same directory as fparser.cc). This file contains generated code and is thus not intended to be to be modified by hand. It was generated by util/bytecoderules_parser, which is available in the development package. */ #define HasInvalidRangesOpcode HasInvalidRangesOpcode::result> #define FP_TRACE_BYTECODE_OPTIMIZATION(srcline,from,to,with) \ /*std::cout << "Changing \"" from "\"\t(line " #srcline ")\n" \ " into \"" to "\"\n" with << std::flush*/ #define FP_TRACE_OPCODENAME(op) \ (op < VarBegin \ ? FP_GetOpcodeName(OPCODE(op)) \ : findName(mData->mNamePtrs,op,NameData::VARIABLE)) #define FP_TRACE_BYTECODE_ADD(opcode) \ /*std::cout << "Adding opcode: " << FP_TRACE_OPCODENAME(opcode) \ << ", bytecode length " << data->ByteCode.size() \ << ", pointer is " << (void*)ByteCodePtr \ << ", code is " << (data->ByteCode.empty() \ ? (void*)0 \ : (void*)&data->ByteCode[0]) \ << std::endl*/ #define qH1 " B" mF #define qG1 gT y*x; #define qF1 hV 2;qI #define qE1 <<"," aD #define qD1 <<"," aB #define qC1 "cNeg" #define qB1 wA"," aD #define qA1 "x[x!=Value_t(0)] " #define q91 <<"," a8 #define q81 wA"," a1 #define q71 );qW q6 #define q61 "cPow " #define q51 "cSqrt" #define q41 "cSqr " #define q31 " cExp2" #define q21 "cExp " #define q11 ){hD wB #define q01 "cCeil" #define mZ "cImag" #define mY "cConj" #define mX "cDup " #define mW hO wB #define mV "cAbs" #define mU wQ wH" " #define mT qS w2 wB #define mS "cFloor" #define mR "cTan" #define mQ " cDup" #define mP "cSin" #define mO (y hX; #define mN "[ y+x]" #define mM hV 2 gC #define mL " cExp" #define mK "A " wX #define mJ "cLess" #define mI "[-x]" wH #define mH "cDiv" a7 #define mG "cLog" #define mF " cDiv" #define mE " " a6 #define mD " " aF #define mC "cMin" #define mB "cMax" #define mA aY"x " #define m9 gN wB #define m8 "x cPow" #define m7 g1 oG wB #define m6 (x);gJ #define m5 "B cSqr" #define m4 oH dE wB #define m3 "[y*x]" wH #define m2 "cGreater" #define m1 mV" " wL #define m0 "cNeg " #define aZ " cAdd" #define aY "y " #define aX "B[IsVarOpcode(B)] " #define aW " cSub" #define aV gY if(dO wB #define aU "cInv" #define aT mX aU #define aS "cAbsNot" #define aR "cLessOrEq" #define aQ "cAdd " q51 #define aP "[y*x] cPow" #define aO "cCos" #define aN "cLog2" #define aM "cCosh" #define aL "cLog10" #define aK "B[B==A]" #define aJ "cNotNot" #define aI " " a2 #define aH "cDup" aZ #define aG "cGreaterOrEq" #define aF "x" aZ #define aE "cEqual" #define aD " " aC #define aC "A" wY #define aB " " wU #define aA " cNeg" #define a9 " cRDiv" #define a8 " B" wY #define a7 " x" wH #define a6 "cRSub" #define a5 "A[IsVarOpcode(A)]" #define a4 "x[x!=Value_t()] " #define a3 " " a5" " #define a2 " with" aD #define a1 " " wG #define a0 " cNot" #define wZ "x[x==Value_t()]" wH #define wY " " wC #define wX "[x]" wH #define wW "cNEqual" #define wV a5 mF #define wU "x = "<Value_t(0)]" #define wP "B[IsNeverNegativeValueOpcode(B)] " #define wO "x[x==Value_t(1)] " #define wN wA"\n" #define wM <<"\n" #define wL "x[x==Value_t(0)] " #define wK "B[IsBinaryOpcode(B)&&!HasInvalidRangesOpcode(B)] " wD #define wJ "A[IsNeverNegativeValueOpcode(A)] " #define wI "A[IsVarOpcode(A)&&mData->mByteCode.size()>2] " #define wH " cMul" #define wG aY"= "<oP){ #define dN mImmed #define dM qE h3 gX #define dL qK dJ w3 #define dK cGreaterOrEq #define dJ =q6; #define dI qK dJ g6 #define dH Value_t #define dG q8 2 gH q4 #define dF q0[0] #define dE qK qR qX #define dD qK qR IsLogicalOpcode(h2 #define dC (qK== #define dB hB oY #define dA qY g8 oG #define d9 pop_back() #define d8 q6;q1 h3 gI #define d7 q8 2 gC #define d6 hR Lba; #define d5 Default4 qU #define d4 :if( #define d3 qV hS d4 qL #define d2 h3 gM if #define d1 IsVarOpcode( #define d0 mData-> #define hZ ]qR w4 #define hY gX Llq #define hX ,x gX Lap #define hW gT y+x;q8 #define hV for qA #define hU gQ cAbs: #define hT unsigned #define hS cAdd #define hR ,y gX #define hQ qL 3 hZ #define hP y=q3-1]qR #define hO y gO #define hN qY if(dP #define hM q6:qC #define hL :if gF #define hK qQ h9 hS hL #define hJ 4 qZ mW(292,aY"cAdd B[IsVarOpcode(B)]" aW mD,mN aZ" B" aW,wA"," a8(B)<<"," a1);q4 #define hI cNeg: #define hH :qS cDup: #define hG hS hH hK h1 wB(310,aH" " aH,"[Value_t(4)]" wH,);q4 #define hF (x!=g1 #define hE qL 2] #define hD B g4 w4 #define hC B=hE qO B)){ #define hB if hF 0)){ #define hA gX Lng; #define h9 qK qV #define h8 }break; #define h7 ()){ #define h6 hR Lap; #define h5 isEvenInteger( #define h4 DegreesToRadians(x #define h3 cMul #define h2 A)){ #define h1 ]==cDup){ #define h0 hI wB(201,m0 mV,mV,);q4 Lab qU qB #define gZ 3 h1 wB(311,aH wH" " aH,"cMul [Value_t(4)]" wH,);q4 #define gY qU hM #define gX );q4 #define gW y,x gX Lba; #define gV IsUnaryOpcode( #define gU g6 w5= #define gT q3-1]= #define gS gR qR IsAlwaysIntegerOpcode(h2 #define gR ;oH dF #define gQ )){qQ h9 #define gP break gR qO A gQ #define gO =q3-1]; #define gN qJ hO #define gM :qJ qC #define gL d2(dO #define gK ]=q6 q9 2 gH #define gJ return; #define gI );w5= #define gH ;qI q5 #define gG qL 2 gK q0-=2; #define gF (qL 2 #define gE y;hT B;hT #define gD d0 mByteCode #define gC ;qI q1 #define gB q9 2 gC h3 gI #define gA ){if gF #define g9 oY h3 dV #define g8 if(x==g1 #define g7 default: #define g6 q5 q0-=1; #define g5 if(!q0){q2 #define g4 =hE qR #define g3 B g4 IsNeverNegativeValueOpcode(B)){ #define g2 &&!HasInvalidRangesOpcode( #define g1 dH( #define g0 FP_ReDefinePointers(); #define qZ ]==q6){ #define qY if(q0[0 qZ qC #define qX IsNeverNegativeValueOpcode(h2 #define qW gD qD #define qV ){case #define qU ;case #define qT }break qU #define qS qQ dF qV #define qR ;if( #define qQ switch( #define qP )&&gD oK> #define qO qR d1 #define qN dF w1 2){ #define qM d0 dN.d9; #define qL q0[- #define qK qL 1] #define qJ oY q6){ #define qI tmp-->0;) #define qH q4 Default0; #define qG }}qH #define qF d0 dN qD #define qE AddFunctionOpcode( #define qD .push_back( #define qC x=q7; #define qB hM wB(132,"x " mV,"[fp_abs(x)]",wN);q4 Lac; #define qA (hT tmp= #define q9 ;hV #define q8 d0 dN.d9 q9 #define q7 q3 0] #define q6 cImmed #define q5 gD.d9; #define q4 goto #define q3 dW[ #define q2 q4 Laa;}case #define q1 q5 qE #define q0 ByteCodePtr hT*q0;dH*dW; #define FP_ReDefinePointers() q0=!gD.empty()?&gD[0]+gD oK-1:0;dW=!d0 dN.empty()?&d0 dN[0]+d0 dN oK-1:0; g0 wE(opcode); #if(!(FP_COMPLEX_VERSION) && !(FP_FLOAT_VERSION)) dH x;hT A;dH gE C;hT D;qQ w5){TailCall_cAbs:g5 cAbs:qS h0 oH dF qR qX wB(393,wJ mV,"A" ,aI(A)wM);gJ qG TailCall_cAdd:g5 hG Lad;qT h3 hL]==hS){if(qL gZ Lae;} h8} q4 dX qU d2 gF h1 wB(313,"cDup" a7 aZ,"[x+Value_t(1)]" wH,wN);q4 Laf;} } q4 dX oF wB(199,qC1 aZ,"cSub" ,);q4 Lag gY hK qZ mW(127,aY"cAdd" mD,"[y+x]" aZ,q81);q4 Lah;qT cRSub:qQ hE d3 3 qZ mW(298,aY"cAdd" mE mD,mN aZ mE,q81);q4 Lai;qT hI wB(299,m0 a6 mD,"[-x]" aZ mE,wN);q4 Laj qU q6:mW(297,aY a6 mD,mN mE,q81);q4 Lak;qT oA Lal;qT hI wB(293,m0"B[IsVarOpcode(B)]" aW mD,"[-x]" aZ" B" aW,wA"," a8(B)wM);q4 Lam qU q6:mW(291,aY"B[IsVarOpcode(B)]" aW mD,mN" B" aW,wA"," a8(B)<<"," a1);q4 Lan;} w9 mW(105,aY aF,"[y+x]" ,q81);q4 Lao;} g8)){wB(57,"x[x==Value_t()]" aZ,,wN);q4 Lap;h8 g7 dX:;A=dF w0 oY cRSub dV wB(290,"x" mE a3"cAdd" ,"[DO_STACKPLUS1] A [x]" aZ mE,aI(A)qD1 wM);incStackPtr();--mStackPtr;q4 Laq;} wB(295,a6 a3"cAdd" ,"[DO_STACKPLUS1] A" aZ mE,aI(A)wM);incStackPtr();--mStackPtr;q4 Lba;} qG TailCall_cAnd:g5 cAnd hH wB(224,mX"cAnd" ,aJ,);q4 Lbb gY m9(117,mA"cAnd" ,"[fp_and(x,y)]" ,q81);q4 Lbc;h8} qH TailCall_cDiv:g5 cDiv hH wB(78,"cDup" mF,"[Value_t()]" wH" [Value_t(1)]" aZ,);q4 w7 if hF gQ hI wB(125,m0 a4"cDiv" ,"[-x]" mF,wN);q4 Lbe qU q6:mW(103,aY a4"cDiv" ,"[y/x]" ,q81);q4 Lbf;} } g8 oG wB(56,wO"cDiv" ,,wN);q4 Lap;h8} qH TailCall_cEqual:g5 w8:dA A=dD wB(421,"A[IsLogicalOpcode(A)] " wO aE,"A" ,qB1(A)wM);q4 Lap;} } m9(115,mA aE,"[fp_equal(y,x)]" ,q81);q4 Lbg;} g8 0 hU wB(359,m1 aE,"[x] " aE,wN);q4 Lbh qU cSqr:wB(361,q41 wL aE,"[x] " aE,wN);q4 Lbh;} wB(411,wL aE,"cNot" ,wN);q4 Lbi;qG TailCall_cGreater:g5 o1:oL hU wB(413,m1 m2,aJ,wN);q4 Lbj;m4(417,wJ wL m2,"A " aJ,qB1(A)wM);q4 Lbk;} } } m9(113,mA m2,"[fp_less(x,y)]" ,q81);q4 Lbl;qG TailCall_cGreaterOrEq:g5 dK:qY g8 1 hU wB(414,mV" " wO aG,aJ,wN);q4 Lbj;m4(418,wJ wO aG,"A " aJ,qB1(A)wM);q4 Lbk;} } } m9(114,mA aG,"[fp_lessOrEq(x,y)]" ,q81);q4 Lbm;qG TailCall_cInv:g5 cInv:qY if hF)){wB(101,a4 aU,"[Value_t(1)/x]" ,wN);q4 Lbn;qG TailCall_cLess:g5 cLess:oL)){A=dE wB(301,wJ wL mJ,mK,qB1(A)wM);oS;} } g8 1 hU wB(415,mV" " wO mJ,"cNot" ,wN);q4 Lbp;m4(419,wJ wO mJ,"A" a0,qB1(A)wM);q4 Lbi;} } } m9(111,mA mJ,"[fp_less(y,x)]" ,q81);q4 Lbq;qG TailCall_cLessOrEq:g5 cLessOrEq:oL hU wB(416,m1 aR,"cNot" ,wN);q4 Lbp;m4(420,wJ wL aR,"A" a0,qB1(A)wM);q4 Lbi;} } } m9(112,mA aR,"[fp_lessOrEq(y,x)]" ,q81);q4 Lca;qG TailCall_cMax:g5 cMax hH wB(60,mX mB,,);q4 w6 m9(141,mA mB,"[fp_max(x,y)]" ,q81);q4 Lcc;} gP cDup:hD wB(66,aK mQ a3 mB,"B" mQ,aI(A)q91(B)wM);q4 Lcb;qT cMax:hD wB(68,aK" " mB a3 mB,"B " mB,aI(A)q91(B)wM);q4 Lcb;h8} qG TailCall_cMin:g5 cMin hH wB(59,mX mC,,);q4 w6 m9(140,mA mC,"[fp_min(x,y)]" ,q81);q4 Lcd;} gP cDup:hD wB(65,aK mQ a3 mC,"B" mQ,aI(A)q91(B)wM);q4 Lcb;qT cMin:hD wB(67,aK" " mC a3 mC,"B " mC,aI(A)q91(B)wM);q4 Lcb;h8} qG TailCall_cMod:g5 cMod:qY if hF)){m9(104,aY a4"cMod" ,"[fp_mod(y,x)]" ,q81);q4 Lce;} qG TailCall_cMul:g5 h3 hH wB(202,"cDup" wH,"cSqr" ,);q4 Lcf oF qQ h9 cDup:wB(467,"cDup" aA wH,"cSqr" aA,);q4 Lcg;oH qK qO A)gA oM B=hQ wB(473,aK wH a3 qC1 wH,m5 wH aA,aI(A)q91(B)wM);q4 Lch;} } } } q4 dY qU cPow gM if gF h1 wB(314,mX m8 wH,"[x+Value_t(1)] cPow" ,wN);q4 Lci;} } q4 dY gY g8 gQ h3:A=hE w0 wB(93,wS" " wZ,wX,qB1(A)wM);q4 Lcj;} q4 Default3;g7 Default3:;A=qK qR IsBinaryOpcode(A)g2 h2 qQ hE qV q6:mW(92,aY wD,wX,qB1(A)<<"," a1);q4 Lck;g7 B g4 IsBinaryOpcode(B)g2 B)){qQ oC qV q6:mW(96,aY wK,mK,qB1(A)q91(B)<<"," a1);q4 Lcl;g7 C=oC qO C)){wB(94,"C[IsVarOpcode(C)] " wK,mK,qB1(A)q91(B)<<", C" wY(C)wM);q4 Lcm;} if(gV C)g2 C)){wB(95,"C[IsUnaryOpcode(C)&&!HasInvalidRangesOpcode(C)] " wK,"B " mK,qB1(A)q91(B)<<", C" wY(C)wM);q4 Lcn;} } } if(d1 B)){wB(90,aX wD,wX,qB1(A)q91(B)wM);q4 Lcj;} if(gV B)g2 B)){wB(91,"B[IsUnaryOpcode(B)&&!HasInvalidRangesOpcode(B)] " wD,mK,qB1(A)q91(B)wM);q4 Lco;} } } if(d1 h2 wB(88,a5" " wZ,"[x]" ,qB1(A)wM);q4 Lcp;} if(gV A)g2 h2 wB(89,"A[IsUnaryOpcode(A)&&!HasInvalidRangesOpcode(A)] " wZ,wX,qB1(A)wM);q4 Lcq;} } } qQ h9 hS:qQ hE qV cDup:wB(317,aH a7,"[x+x]" wH,wN);q4 Lda qU o5 3 qZ hO A=qL 4]w0 wB(386,a5" y" wH aZ a7,wX" A " m3 aZ,wA", " aY"= " <mByteCode.size()>1] A[IsUnaryOpcode(A)]" wH,"D C cSqr" wH,aI(A)q91(B)<<", C" wY(C)<<", D" wY(D)wM);q4 Ldm;} } } } qG TailCall_cNEqual:g5 cNEqual:dA A=dD wB(422,"A[IsLogicalOpcode(A)] " wO wW,"A" a0,qB1(A)wM);q4 Lbi;} } m9(116,mA wW,"[fp_nequal(y,x)]" ,q81);q4 Ldn;} g8 0 hU wB(360,m1 wW,"[x] " wW,wN);q4 Ldo qU cSqr:wB(362,q41 wL wW,"[x] " wW,wN);q4 Ldo;} wB(412,wL wW,aJ,wN);q4 Lbk;qG TailCall_cNeg:g5 hI qS h3 gM wB(123,"x" wH aA,mI,wN);q4 Ldp;qT hI wB(61,qC1 aA,,);q4 w6 wB(100,"x" aA,"[-x]" ,wN);q4 Ldq;} qH TailCall_cNot:g5 cNot:qS cAbs:wB(227,mV a0,"cNot" ,);q4 Lea qU cAbsNot:A=dD wB(389,"A[IsLogicalOpcode(A)] " aS a0,"A" ,aI(A)wM);q4 Lcb;} if(A!=q6){wB(390,"A[A!=cImmed] " aS a0,"A cAbsNotNot" ,aI(A)wM);q4 Leb;} q4 o0 qU cAbsNotNot:wB(231,"cAbsNotNot" a0,aS,);q4 Lec qU hS gM wB(424,aF a0,"[-x] " aE,wN);q4 Led;} q4 o0 qU w8:wB(220,aE a0,wW,);q4 Lee qU o1:wB(218,m2 a0,aR,);q4 Lef qU dK:wB(219,aG a0,mJ,);q4 Leg qU cLess:wB(216,mJ a0,aG,);q4 Leh qU cLessOrEq:wB(217,aR a0,m2,);q4 Lei qU cNEqual:wB(221,wW a0,aE,);q4 Lej oF wB(226,qC1 a0,"cNot" ,);q4 Lea qU cNot:wB(229,"cNot" a0,aJ,);q4 Lbb qU dS:wB(230,aJ a0,"cNot" ,);q4 Lea gY wB(107,"x" a0,"[fp_not(x)]" ,wN);q4 Lek;g7 o0:;A=dF qR qX wB(391,wJ"cNot" ,"A " aS,aI(A)wM);q4 Lel;qG TailCall_cNotNot:g5 dS:qS hS gM wB(423,aF" " aJ,"[-x] " wW,wN);q4 Lem;qT cNot:wB(232,"cNot " aJ,"cNot" ,);gJ} qH TailCall_cOr:g5 cOr hH wB(223,mX"cOr" ,aJ,);q4 Lbb gY m9(118,mA"cOr" ,"[fp_or(x,y)]" ,q81);q4 Len;h8} qH TailCall_cRDiv:g5 cRDiv:dA wB(268,wO"cRDiv" ,aU,wN);q4 Leo;qG TailCall_cRSub:g5 cRSub d4 q0[0 h1 wB(77,"cDup" mE,"[Value_t()]" wH,);q4 Lep;} qH TailCall_cSqr:g5 cSqr:qS cAbs:wB(204,mV" cSqr" ,"cSqr" ,);q4 Leq oF wB(203,m0"cSqr" ,"cSqr" ,);q4 Leq;} qH TailCall_cSub:g5 cSub hH wB(76,"cDup" aW,"[Value_t()]" wH,);q4 Lep oF wB(200,qC1 aW,"cAdd" ,);q4 Lfa gY g8)){wB(58,"x[x==Value_t()]" aW,,wN);q4 Lap;} m9(106,aY"x" aW,"[y-x]" ,q81);q4 Lfb;} wB(51,"x" aW,"[-x]" aZ,wN);q4 Lfc gR w0 oY cRSub dV wB(289,"x" mE a3"cSub" ,"A" aZ" [x]" mE,aI(A)qD1 wM);q4 Lfd;} wB(296,a6 a3"cSub" ,"[DO_STACKPLUS1] A" aW mE,aI(A)wM);incStackPtr();--mStackPtr;q4 Lfe;} qG g7 Default0:;A=w5 qR IsComparisonOpcode(h2 qY hK qZ mW(364,aY"cAdd" wF,"[x-y] A" ,aI(A)qD1<<"," a1);q4 Lff;qT hI wB(365,qC1 wF,"[-x] {OppositeComparisonOpcode(A)}" ,aI(A)qD1 wM);q4 Lfg;} } } if(d1 A qP 0){B=q0[0 hZ wB(475,aK" A[IsVarOpcode(A)&&mData->mByteCode.size()>0]" ,"B" mQ,aI(A)q91(B)wM);q4 Lfh;} } if(gV h2 B=dF qO B qP 1){C=qK qR C==A){D g4 D==B){wB(476,"D[D==B] C[C==A] B[IsVarOpcode(B)&&mData->mByteCode.size()>1] A[IsUnaryOpcode(A)]" ,"D C" mQ,aI(A)q91(B)<<", C" wY(C)<<", D" wY(D)wM);q4 Lfi;} } } } } q4 Laa;Laa:qW w5);gJ Lab:g6 wE(cAbs);q4 TailCall_cAbs;Lac:q7=dP;gJ Lad:oZ 4));gG Lfj:w5=h3;Lfk:g0 Lfl:wE(cMul);q4 TailCall_cMul;Lae:hV 4 dT oZ 4)q71 gX Lfj;Laf:q7=x+g1 1);gG Lbo:w5=h3;q4 Lfl;Lag:gU cSub;Lfm:wE(cSub);q4 TailCall_cSub;Lah:hW 2 gH Lfn:g0 Lfo:wE(cAdd);q4 TailCall_cAdd;Lai:hW oR Lfp:qE hS);Lfq:w5=cRSub;g0 wE(cRSub);q4 TailCall_cRSub;Laj:o9;qL 2 gK q4 Lfp;Lak:hW 2 gH q4 Lfq;Lal:hW 4 gH Lga:qE hS);Lgb:qE B);Lgc:w5=cSub;g0 q4 Lfm;Lam:o9;oC=q6 q9 oR q4 Lga;Lan:hW oR q4 Lgb;Lao:gT y+x;Lap:qM Lcb:q5 gJ Laq:q8 oV o7 x q71 gX Lfp;Lba:mM A gX Lfp;Lbb:gU dS;Lgd:wE(cNotNot);q4 TailCall_cNotNot;Lbc:gT fp_and(x h6 Lbd:oZ));dF dJ qE dU oZ 1));Lge:qW q6);Lgf:w5=hS;q4 Lfn;Lbe:o9;dI wE(cDiv);q4 TailCall_cDiv;Lbf:gT y/x;q4 Lap;Lbg:gT fp_equal mO Lbh:dI Lgg:wE(cEqual);q4 TailCall_cEqual;Lbi:qM q5 Lgh:w5=cNot;g0 Lgi:wE(cNot);q4 TailCall_cNot;Lbj:q8 2 gH Lgj:w5=dS;g0 q4 Lgd;Lbk:qM w3 Lgj;Lbl:gT fp_less(x h6 Lbm:gT fp_lessOrEq(x h6 Lbn:q7=g1 1)/x;gJ Lbp:dG Lgh;Lbq:gT fp_less mO Lca:gT fp_lessOrEq mO Lcc:gT fp_max(x h6 Lcd:gT fp_min(x h6 Lce:gT fp_mod mO Lcf:gU cSqr;Lgk:wE(cSqr);q4 TailCall_cSqr;Lcg:mM cSqr);Lgl:w5=cNeg;g0 wE(cNeg);q4 TailCall_cNeg;Lch:hV 3 gC cSqr);dM Lgl;Lci:q7=x+g1 1);hE=q6 q9 2 gC cPow);gJ Lcj:gG q4 Lfl;Lck:gT x;Lgm:dG Lfk;Lcl:qF1 qM Lgn:hV 4 gH Lgo:o6 x);Lgp:qW q6 gX Lfk;Lcm:qM q4 Lgn;Lcn:q8 4 gC B gX Lgo;Lco:q8 oR q4 Lgo;Lcp:qK dJ q4 Lcb;Lcq:dI q4 Lfl;Lda:q7=x+x;q4 Lcj;Ldb:gT x;qL 4]dJ q8 4 dT o6 y*x q71);dM Lgf;Ldc:gT x;d7 dU qF y*x gX Lge;Ldd:q8 4 dT qF x+x gX Lgp;Lde:qF1 q8 oR gJ Ldf:qG1 q4 Lgm;Ldg:o9;q4 Lcq;Ldh:gT x;qL 4]dJ q8 4 dT o6 y*x q71);dM Lgc;Ldi:qG1 q4 Lap;Ldj:qM w3 Lgl;Ldk:dF=cDup;dW-=1;qM Lgq:w5=hS;q4 Lfo;Ldl:hV 2 gH Lha:qE cSqr gX Lfk;Ldm:hV oR q4 Lha;Ldn:gT fp_nequal mO Ldo:dI Lhb:wE(cNEqual);q4 TailCall_cNEqual;Ldp:o9;g6 oS;Ldq:o9;gJ Lea:g6 q4 Lgi;Leb:q1 cAbsNotNot);gJ Lec:q5 Lel:qE cAbsNot);gJ Led:o9;Lej:gU w8;q4 Lgg;Lee:gU cNEqual;q4 Lhb;Lef:gU cLessOrEq;wE(cLessOrEq);q4 TailCall_cLessOrEq;Leg:gU cLess;wE(cLess);q4 TailCall_cLess;Leh:gU dK;wE(cGreaterOrEq);q4 TailCall_cGreaterOrEq;Lei:gU o1;wE(cGreater);q4 TailCall_cGreater;Lek:q7=fp_not m6 Lem:o9;q4 Lee;Len:gT fp_or(x h6 Leo:qM q5 w5=cInv;g0 wE(cInv);q4 TailCall_cInv;Lep:oZ));dF dJ q4 Lfj;Leq:g6 q4 Lgk;Lfa:g6 q4 Lgq;Lfb:gT y-x;q4 Lap;Lfc:o9;q4 Lgq;Lfd:q8 oV oJ hS o7 x q71 gX Lfq;Lfe:mM A oJ cSub gX Lfq;Lff:gT x-y;d7 A);gJ Lfg:o9;qK dJ q1 OppositeComparisonOpcode(A));gJ Lfh:qW cDup);gJ Lfi:dF=cDup;gJ gJ q4 TailCall_cAnd;q4 TailCall_cMax;q4 TailCall_cMin;q4 TailCall_cMod;q4 TailCall_cNeg;q4 TailCall_cOr;q4 TailCall_cRDiv;q4 TailCall_cSub; #endif #if((FP_COMPLEX_VERSION) && !(FP_FLOAT_VERSION)) dH x;dH gE A;hT C;hT D;qQ w5){TailCall_cAbs:g5 cAbs:qS h0} qH TailCall_cAdd:g5 hG Lad;qT h3 hL]==hS){if(qL gZ Lae;} h8} q4 dX qU d2 gF h1 wB(313,"cDup" a7 aZ,"[x+Value_t(1)]" wH,wN);q4 Laf;} } q4 dX oF wB(199,qC1 aZ,"cSub" ,);q4 Lag gY hK qZ mW(127,aY"cAdd" mD,"[y+x]" aZ,q81);q4 Lah;qT cRSub:qQ hE d3 3 qZ mW(298,aY"cAdd" mE mD,mN aZ mE,q81);q4 Lai;qT hI wB(299,m0 a6 mD,"[-x]" aZ mE,wN);q4 Laj qU q6:mW(297,aY a6 mD,mN mE,q81);q4 Lak;qT oA Lal;qT hI wB(293,m0"B[IsVarOpcode(B)]" aW mD,"[-x]" aZ" B" aW,wA"," a8(B)wM);q4 Lam qU q6:mW(291,aY"B[IsVarOpcode(B)]" aW mD,mN" B" aW,wA"," a8(B)<<"," a1);q4 Lan;} w9 mW(105,aY aF,"[y+x]" ,q81);q4 Lao;} g8)){wB(57,"x[x==Value_t()]" aZ,,wN);q4 Lap;h8 g7 dX:;A=dF w0 oY cRSub dV wB(290,"x" mE a3"cAdd" ,"[DO_STACKPLUS1] A [x]" aZ mE,aI(A)qD1 wM);incStackPtr();--mStackPtr;q4 Laq;} wB(295,a6 a3"cAdd" ,"[DO_STACKPLUS1] A" aZ mE,aI(A)wM);incStackPtr();--mStackPtr;q4 Lba;} qG TailCall_cAnd:g5 cAnd hH wB(224,mX"cAnd" ,aJ,);q4 Lbb gY m9(117,mA"cAnd" ,"[fp_and(x,y)]" ,q81);q4 Lbc;h8} qH TailCall_cConj:g5 cConj:qS cConj:wB(63,mY" " mY,,);q4 w7 wB(193,"x " mY,"[fp_conj(x)]" ,wN);q4 Lbe;} qH TailCall_cDiv:g5 cDiv hH wB(78,"cDup" mF,"[Value_t()]" wH" [Value_t(1)]" aZ,);q4 Lbf gY if hF gQ hI wB(125,m0 a4"cDiv" ,"[-x]" mF,wN);q4 Lbg qU q6:mW(103,aY a4"cDiv" ,"[y/x]" ,q81);q4 Lbh;} } g8 oG wB(56,wO"cDiv" ,,wN);q4 Lap;h8} qH TailCall_cEqual:g5 w8:dA A=dD wB(421,"A[IsLogicalOpcode(A)] " wO aE,"A" ,qB1(A)wM);q4 Lap;} } m9(115,mA aE,"[fp_equal(y,x)]" ,q81);q4 Lbi;} g8 0 hU wB(359,m1 aE,"[x] " aE,wN);q4 Lbj qU cSqr:wB(361,q41 wL aE,"[x] " aE,wN);q4 Lbj;} wB(411,wL aE,"cNot" ,wN);q4 Lbk;qG TailCall_cGreater:g5 o1:qY m9(113,mA m2,"[fp_less(x,y)]" ,q81);q4 Lbl;qG TailCall_cGreaterOrEq:g5 dK:qY m9(114,mA aG,"[fp_lessOrEq(x,y)]" ,q81);q4 Lbm;qG TailCall_cImag:g5 cImag:qS cAbs:wB(81,mV" " mZ,"[Value_t()]" wH,);q4 Lbn qU cReal:wB(80,"cReal " mZ,"[Value_t()]" wH,);q4 Lbn gY wB(192,"x " mZ,"[fp_imag(x)]" ,wN);oS;} qH TailCall_cInv:g5 cInv:qY if hF)){wB(101,a4 aU,"[Value_t(1)/x]" ,wN);q4 Lbp;qG TailCall_cLess:g5 cLess:oL)){A=dE wB(301,wJ wL mJ,mK,qB1(A)wM);q4 Lbq;} } m9(111,mA mJ,"[fp_less(y,x)]" ,q81);q4 Lca;qG TailCall_cLessOrEq:g5 cLessOrEq:qY m9(112,mA aR,"[fp_lessOrEq(y,x)]" ,q81);q4 Lcb;qG TailCall_cMax:g5 cMax hH wB(60,mX mB,,);q4 w7 m9(141,mA mB,"[fp_max(x,y)]" ,q81);q4 Lcc;} gP cDup:hD wB(66,aK mQ a3 mB,"B" mQ,aI(A)q91(B)wM);q4 Lbd;qT cMax:hD wB(68,aK" " mB a3 mB,"B " mB,aI(A)q91(B)wM);q4 Lbd;h8} qG TailCall_cMin:g5 cMin hH wB(59,mX mC,,);q4 w7 m9(140,mA mC,"[fp_min(x,y)]" ,q81);q4 Lcd;} gP cDup:hD wB(65,aK mQ a3 mC,"B" mQ,aI(A)q91(B)wM);q4 Lbd;qT cMin:hD wB(67,aK" " mC a3 mC,"B " mC,aI(A)q91(B)wM);q4 Lbd;h8} qG TailCall_cMod:g5 cMod:qY if hF)){m9(104,aY a4"cMod" ,"[fp_mod(y,x)]" ,q81);q4 Lce;} qG TailCall_cMul:g5 h3 hH wB(202,"cDup" wH,"cSqr" ,);q4 Lcf oF qQ h9 cDup:wB(467,"cDup" aA wH,"cSqr" aA,);q4 Lcg;oH qK qO A)gA oM B=hQ wB(473,aK wH a3 qC1 wH,m5 wH aA,aI(A)q91(B)wM);q4 Lch;} } } } q4 dY qU cPow gM if gF h1 wB(314,mX m8 wH,"[x+Value_t(1)] cPow" ,wN);q4 Lci;} } q4 dY gY g8 gQ h3:A=hE w0 wB(93,wS" " wZ,wX,qB1(A)wM);q4 Lcj;} q4 Default3;g7 Default3:;A=qK qR IsBinaryOpcode(A)g2 h2 qQ hE qV q6:mW(92,aY wD,wX,qB1(A)<<"," a1);q4 Lck;g7 B g4 IsBinaryOpcode(B)g2 B)){qQ oC qV q6:mW(96,aY wK,mK,qB1(A)q91(B)<<"," a1);q4 Lcl;g7 C=oC qO C)){wB(94,"C[IsVarOpcode(C)] " wK,mK,qB1(A)q91(B)<<", C" wY(C)wM);q4 Lcm;} if(gV C)g2 C)){wB(95,"C[IsUnaryOpcode(C)&&!HasInvalidRangesOpcode(C)] " wK,"B " mK,qB1(A)q91(B)<<", C" wY(C)wM);q4 Lcn;} } } if(d1 B)){wB(90,aX wD,wX,qB1(A)q91(B)wM);q4 Lcj;} if(gV B)g2 B)){wB(91,"B[IsUnaryOpcode(B)&&!HasInvalidRangesOpcode(B)] " wD,mK,qB1(A)q91(B)wM);q4 Lco;} } } if(d1 h2 wB(88,a5" " wZ,"[x]" ,qB1(A)wM);q4 Lcp;} if(gV A)g2 h2 wB(89,"A[IsUnaryOpcode(A)&&!HasInvalidRangesOpcode(A)] " wZ,wX,qB1(A)wM);q4 Lcq;} } } qQ h9 hS:qQ hE qV cDup:wB(317,aH a7,"[x+x]" wH,wN);q4 Lda qU o5 3 qZ hO A=qL 4]w0 wB(386,a5" y" wH aZ a7,wX" A " m3 aZ,wA", " aY"= " <mByteCode.size()>1] A[IsUnaryOpcode(A)]" wH,"D C cSqr" wH,aI(A)q91(B)<<", C" wY(C)<<", D" wY(D)wM);q4 Ldm;} } } } qG TailCall_cNEqual:g5 cNEqual:dA A=dD wB(422,"A[IsLogicalOpcode(A)] " wO wW,"A" a0,qB1(A)wM);q4 Lbk;} } m9(116,mA wW,"[fp_nequal(y,x)]" ,q81);q4 Ldn;} g8 0 hU wB(360,m1 wW,"[x] " wW,wN);q4 Ldo qU cSqr:wB(362,q41 wL wW,"[x] " wW,wN);q4 Ldo;} wB(412,wL wW,aJ,wN);q4 Ldp;qG TailCall_cNeg:g5 hI qS h3 gM wB(123,"x" wH aA,mI,wN);q4 Ldq;qT hI wB(61,qC1 aA,,);q4 w7 wB(100,"x" aA,"[-x]" ,wN);q4 Lea;} qH TailCall_cNot:g5 cNot:qS cAbsNotNot:wB(231,"cAbsNotNot" a0,aS,);q4 Leb qU hS gM wB(424,aF a0,"[-x] " aE,wN);q4 Lec;qT w8:wB(220,aE a0,wW,);q4 Led qU o1:wB(218,m2 a0,aR,);q4 Lee qU dK:wB(219,aG a0,mJ,);q4 Lef qU cLess:wB(216,mJ a0,aG,);q4 Leg qU cLessOrEq:wB(217,aR a0,m2,);q4 Leh qU cNEqual:wB(221,wW a0,aE,);q4 Lei qU cNot:wB(229,"cNot" a0,aJ,);q4 Lbb qU dS:wB(230,aJ a0,"cNot" ,);q4 Lej gY wB(107,"x" a0,"[fp_not(x)]" ,wN);q4 Lek;} qH TailCall_cNotNot:g5 dS:qS hS gM wB(423,aF" " aJ,"[-x] " wW,wN);q4 Lel;qT cNot:wB(232,"cNot " aJ,"cNot" ,);gJ} qH TailCall_cOr:g5 cOr hH wB(223,mX"cOr" ,aJ,);q4 Lbb gY m9(118,mA"cOr" ,"[fp_or(x,y)]" ,q81);q4 Lem;h8} qH TailCall_cRDiv:g5 cRDiv:dA wB(268,wO"cRDiv" ,aU,wN);q4 Len;qG TailCall_cRSub:g5 cRSub d4 q0[0 h1 wB(77,"cDup" mE,"[Value_t()]" wH,);q4 Lbn;} qH TailCall_cReal:g5 cReal:qY wB(191,"x cReal" ,"[fp_real(x)]" ,wN);q4 Leo;} qH TailCall_cSqr:g5 cSqr:qS cAbs:wB(204,mV" cSqr" ,"cSqr" ,);q4 Lep oF wB(203,m0"cSqr" ,"cSqr" ,);q4 Lep;} qH TailCall_cSub:g5 cSub hH wB(76,"cDup" aW,"[Value_t()]" wH,);q4 Lbn oF wB(200,qC1 aW,"cAdd" ,);q4 Leq gY g8)){wB(58,"x[x==Value_t()]" aW,,wN);q4 Lap;} m9(106,aY"x" aW,"[y-x]" ,q81);q4 Lfa;} wB(51,"x" aW,"[-x]" aZ,wN);q4 Lfb gR w0 oY cRSub dV wB(289,"x" mE a3"cSub" ,"A" aZ" [x]" mE,aI(A)qD1 wM);q4 Lfc;} wB(296,a6 a3"cSub" ,"[DO_STACKPLUS1] A" aW mE,aI(A)wM);incStackPtr();--mStackPtr;q4 Lfd;} qG g7 Default0:;A=w5 w1 0){B=q0[0 hZ wB(475,aK" A[IsVarOpcode(A)&&mData->mByteCode.size()>0]" ,"B" mQ,aI(A)q91(B)wM);q4 Lfe;} } if(gV h2 B=dF qO B qP 1){C=qK qR C==A){D g4 D==B){wB(476,"D[D==B] C[C==A] B[IsVarOpcode(B)&&mData->mByteCode.size()>1] A[IsUnaryOpcode(A)]" ,"D C" mQ,aI(A)q91(B)<<", C" wY(C)<<", D" wY(D)wM);q4 Lff;} } } } } q4 Laa;Laa:qW w5);gJ Lab:g6 wE(cAbs);q4 TailCall_cAbs;Lac:q7=dP;gJ Lad:oZ 4));gG Lfg:w5=h3;Lfh:g0 Lfi:wE(cMul);q4 TailCall_cMul;Lae:hV 4 dT oZ 4)q71 gX Lfg;Laf:q7=x+g1 1);gG Lbq:w5=h3;q4 Lfi;Lag:gU cSub;Lfj:wE(cSub);q4 TailCall_cSub;Lah:hW 2 gH Lfk:g0 Lfl:wE(cAdd);q4 TailCall_cAdd;Lai:hW oR Lfm:qE hS);Lfn:w5=cRSub;g0 wE(cRSub);q4 TailCall_cRSub;Laj:o9;qL 2 gK q4 Lfm;Lak:hW 2 gH q4 Lfn;Lal:hW 4 gH Lfo:qE hS);Lfp:qE B);Lfq:w5=cSub;g0 q4 Lfj;Lam:o9;oC=q6 q9 oR q4 Lfo;Lan:hW oR q4 Lfp;Lao:gT y+x;Lap:qM Lbd:q5 gJ Laq:q8 oV o7 x q71 gX Lfm;Lba:mM A gX Lfm;Lbb:gU dS;Lga:wE(cNotNot);q4 TailCall_cNotNot;Lbc:gT fp_and(x h6 Lbe:q7=fp_conj m6 Lbf:oZ));dF dJ qE dU oZ 1));Lgb:qW q6);Lgc:w5=hS;q4 Lfk;Lbg:o9;dI wE(cDiv);q4 TailCall_cDiv;Lbh:gT y/x;q4 Lap;Lbi:gT fp_equal mO Lbj:dI Lgd:wE(cEqual);q4 TailCall_cEqual;Lbk:qM q5 w5=cNot;g0 Lge:wE(cNot);q4 TailCall_cNot;Lbl:gT fp_less(x h6 Lbm:gT fp_lessOrEq(x h6 Lbn:oZ));dF dJ q4 Lfg;Lbo:q7=fp_imag m6 Lbp:q7=g1 1)/x;gJ Lca:gT fp_less mO Lcb:gT fp_lessOrEq mO Lcc:gT fp_max(x h6 Lcd:gT fp_min(x h6 Lce:gT fp_mod mO Lcf:gU cSqr;Lgf:wE(cSqr);q4 TailCall_cSqr;Lcg:mM cSqr);Lgg:w5=cNeg;g0 wE(cNeg);q4 TailCall_cNeg;Lch:hV 3 gC cSqr);dM Lgg;Lci:q7=x+g1 1);hE=q6 q9 2 gC cPow);gJ Lcj:gG q4 Lfi;Lck:gT x;Lgh:dG Lfh;Lcl:qF1 qM Lgi:hV 4 gH Lgj:o6 x);Lgk:qW q6 gX Lfh;Lcm:qM q4 Lgi;Lcn:q8 4 gC B gX Lgj;Lco:q8 oR q4 Lgj;Lcp:qK dJ q4 Lbd;Lcq:dI q4 Lfi;Lda:q7=x+x;q4 Lcj;Ldb:gT x;qL 4]dJ q8 4 dT o6 y*x q71);dM Lgc;Ldc:gT x;d7 dU qF y*x gX Lgb;Ldd:q8 4 dT qF x+x gX Lgk;Lde:qF1 q8 oR gJ Ldf:qG1 q4 Lgh;Ldg:o9;q4 Lcq;Ldh:gT x;qL 4]dJ q8 4 dT o6 y*x q71);dM Lfq;Ldi:qG1 q4 Lap;Ldj:qM w3 Lgg;Ldk:dF=cDup;dW-=1;qM Lgl:w5=hS;q4 Lfl;Ldl:hV 2 gH Lgm:qE cSqr gX Lfh;Ldm:hV oR q4 Lgm;Ldn:gT fp_nequal mO Ldo:dI Lgn:wE(cNEqual);q4 TailCall_cNEqual;Ldp:qM q5 w5=dS;g0 q4 Lga;Ldq:o9;g6 q4 Lbq;Lea:o9;gJ Leb:q1 cAbsNot);gJ Lec:o9;Lei:gU w8;q4 Lgd;Led:gU cNEqual;q4 Lgn;Lee:gU cLessOrEq;wE(cLessOrEq);q4 TailCall_cLessOrEq;Lef:gU cLess;wE(cLess);q4 TailCall_cLess;Leg:gU dK;wE(cGreaterOrEq);q4 TailCall_cGreaterOrEq;Leh:gU o1;wE(cGreater);q4 TailCall_cGreater;Lej:g6 q4 Lge;Lek:q7=fp_not m6 Lel:o9;q4 Led;Lem:gT fp_or(x h6 Len:qM q5 w5=cInv;g0 wE(cInv);q4 TailCall_cInv;Leo:q7=fp_real m6 Lep:g6 q4 Lgf;Leq:g6 q4 Lgl;Lfa:gT y-x;q4 Lap;Lfb:o9;q4 Lgl;Lfc:q8 oV oJ hS o7 x q71 gX Lfn;Lfd:mM A oJ cSub gX Lfn;Lfe:qW cDup);gJ Lff:dF=cDup;gJ gJ q4 TailCall_cAnd;q4 TailCall_cConj;q4 TailCall_cImag;q4 TailCall_cMax;q4 TailCall_cMin;q4 TailCall_cMod;q4 TailCall_cNeg;q4 TailCall_cOr;q4 TailCall_cRDiv;q4 TailCall_cReal;q4 TailCall_cSub; #endif #if((FP_FLOAT_VERSION) && !(FP_COMPLEX_VERSION)) dH x;hT A;dH gE C;hT D;qQ w5){TailCall_cAbs:g5 cAbs:qS h0 oH dF qR qX wB(393,wJ mV,"A" ,aI(A)wM);gJ qG TailCall_cAcos:g5 cAcos:hN<=m7(148,"x[fp_abs(x)<=Value_t(1)] cAcos" ,"[fp_acos(x)]" ,wN);q4 Lad;qG TailCall_cAcosh:g5 cAcosh:qY if(x>=m7(145,"x[x>=Value_t(1)] cAcosh" ,"[fp_acosh(x)]" ,wN);q4 Lae;qG TailCall_cAdd:g5 hG Laf;qT h3 hL]==hS){if(qL gZ Lag;} h8} q4 dX qU d2 gF h1 wB(313,"cDup" a7 aZ,"[x+Value_t(1)]" wH,wN);q4 Lah;} } q4 dX oF wB(199,qC1 aZ,"cSub" ,);q4 Lai gY hK qZ mW(127,aY"cAdd" mD,"[y+x]" aZ,q81);q4 Laj;qT cRSub:qQ hE d3 3 qZ mW(298,aY"cAdd" mE mD,mN aZ mE,q81);q4 Lak;qT hI wB(299,m0 a6 mD,"[-x]" aZ mE,wN);q4 Lal qU q6:mW(297,aY a6 mD,mN mE,q81);q4 Lam;qT oA Lan;qT hI wB(293,m0"B[IsVarOpcode(B)]" aW mD,"[-x]" aZ" B" aW,wA"," a8(B)wM);q4 Lao qU q6:mW(291,aY"B[IsVarOpcode(B)]" aW mD,mN" B" aW,wA"," a8(B)<<"," a1);q4 Lap;} w9 mW(105,aY aF,"[y+x]" ,q81);q4 Laq;} g8)){wB(57,"x[x==Value_t()]" aZ,,wN);q4 Lba;h8 g7 dX:;A=dF w0 oY cRSub dV wB(290,"x" mE a3"cAdd" ,"[DO_STACKPLUS1] A [x]" aZ mE,aI(A)qD1 wM);incStackPtr();--mStackPtr;q4 Lbb;} wB(295,a6 a3"cAdd" ,"[DO_STACKPLUS1] A" aZ mE,aI(A)wM);incStackPtr();--mStackPtr;q4 Lbc;} qG TailCall_cAnd:g5 cAnd hH wB(224,mX"cAnd" ,aJ,);q4 w7 m9(117,mA"cAnd" ,"[fp_and(x,y)]" ,q81);q4 Lbe;h8} qH TailCall_cAsin:g5 cAsin:hN<=m7(149,"x[fp_abs(x)<=Value_t(1)] cAsin" ,"[fp_asin(x)]" ,wN);q4 Lbf;qG TailCall_cAsinh:g5 cAsinh:qY wB(146,"x cAsinh" ,"[fp_asinh(x)]" ,wN);q4 Lbg;} qH TailCall_cAtan:g5 cAtan:qY wB(150,"x cAtan" ,"[fp_atan(x)]" ,wN);q4 Lbh;} qH TailCall_cAtan2:g5 cAtan2:qY m9(139,mA"cAtan2" ,"[fp_atan2(y,x)]" ,q81);q4 Lbi;qG TailCall_cAtanh:g5 cAtanh:hN()]" wH" " wR,"cDeg" ,q81);q4 Ldd;} if((y/x)==fp_const_deg_to_rad h7 wB(322,"y[(y/x)==fp_const_deg_to_rad()]" wH" " wR,"cRad" ,q81);q4 Lde;} wB(323,"y" wH" " wR,"[y/x]" wH,q81);q4 Ldf;} } wB(325,wR,"[Value_t(1)/x]" wH,wN);q4 Ldg;} gP cDiv:hC wB(271,aX"cDiv " wV,"[DO_STACKPLUS1] B A" wH mF,aI(A)q91(B)wM);incStackPtr();--mStackPtr;q4 Ldh;qT cRDiv:qQ hE qV hM wB(266,"x" a9" " wV,"A" wH" [x]" a9,aI(A)qD1 wM);q4 Ldi;g7 hC wB(265,"B[IsVarOpcode(B)]" a9" " wV,"A" wH" B" a9,aI(A)q91(B)wM);q4 Ldj;} h8} qG TailCall_cEqual:g5 w8:oL hU wB(359,m1 aE,"[x] " aE,wN);q4 Ldk qU cSqr:wB(361,q41 wL aE,"[x] " aE,wN);q4 Ldk;} } m9(115,mA aE,"[fp_equal(y,x)]" ,q81);q4 Ldl;qG TailCall_cExp:g5 w2 qS hS gM wB(404,aF mL,q21"[fp_exp(x)]" wH,wN);q4 Ldm;qT cLog:A=dE wB(340,wJ mG mL,"A" ,aI(A)wM);q4 oN hM wB(154,"x" mL,"[fp_exp(x)]" ,wN);q4 Ldo;} qH TailCall_cExp2:g5 cExp2:qS hS gM wB(405,aF q31,"cExp2 [fp_exp2(x)]" wH,wN);q4 Ldp;qT cLog2:A=dE wB(341,wJ aN q31,"A" ,aI(A)wM);q4 oN hM wB(155,"x" q31,"[fp_exp2(x)]" ,wN);q4 Ldq;} wB(479,"cExp2" ,"[DO_STACKPLUS1] [fp_log(Value_t(2))]" wH mL,);incStackPtr();--mStackPtr;q4 Lea;TailCall_cFloor:g5 cFloor:qS hI wB(401,m0 mS,q01 aA,);q4 Leb gY wB(136,"x " mS,"[fp_floor(x)]" ,wN);q4 Lec gS wB(395,"A[IsAlwaysIntegerOpcode(A)] " mS,"A" ,aI(A)wM);gJ qG TailCall_cGreater:g5 o1:qY m9(113,mA m2,"[fp_less(x,y)]" ,q81);q4 Led;} g8-oO wB(431,"x[x==Value_t(-0.5)] " m2,m0 aS,wN);q4 Lee;qG TailCall_cGreaterOrEq:g5 dK:qY dB cAbs){wB(427,mV" x[x!=Value_t(0)] " aG,"[Value_t(0.5)/x]" wH" " aJ,wN);q4 Lef;} } m9(114,mA aG,"[fp_lessOrEq(x,y)]" ,q81);q4 Leg;} g8 oO wB(430,"x[x==Value_t(0.5)] " aG,"cAbsNotNot" ,wN);q4 Leh;qG TailCall_cHypot:g5 cHypot d4 dF==cSinCos){wB(84,"cSinCos cHypot" ,"[Value_t()]" wH" [Value_t(1)]" aZ,);q4 Lci;} qH TailCall_cInt:g5 cInt:qS hM wB(137,"x cInt" ,"[fp_int(x)]" ,wN);q4 Lei gS wB(397,"A[IsAlwaysIntegerOpcode(A)] cInt" ,"A" ,aI(A)wM);gJ qG TailCall_cInv:g5 cInv:qS cCos:wB(256,aO" " aU,"cSec" ,);q4 Lej qU cCot:wB(260,"cCot " aU,mR,);q4 Lcp qU cCsc:wB(258,"cCsc " aU,mP,);q4 Lek qU cInv:wB(62,aU" " aU,,);q4 Ldn qU cPow:wB(355,q61 aU,m0"cPow" ,);q4 Lel qU cSec:wB(259,"cSec " aU,aO,);q4 Lem qU cSin:wB(255,mP" " aU,"cCsc" ,);q4 Len qU cSqrt:wB(206,q51" " aU,"cRSqrt" ,);q4 Leo qU cTan:wB(257,mR" " aU,"cCot" ,);q4 Lep gY if hF)){wB(101,a4 aU,"[Value_t(1)/x]" ,wN);q4 Leq;h8} qH TailCall_cLess:g5 cLess:oL)){A=dE wB(301,wJ wL mJ,mK,qB1(A)wM);q4 Lfa;} } dB cAbs){wB(426,mV" x[x!=Value_t(0)] " mJ,"[Value_t(0.5)/x]" wH a0,wN);q4 Lfb;} } m9(111,mA mJ,"[fp_less(y,x)]" ,q81);q4 Lfc;} g8 oO wB(429,"x[x==Value_t(0.5)] " mJ,aS,wN);q4 Lfd;qG TailCall_cLessOrEq:g5 cLessOrEq:qY m9(112,mA aR,"[fp_lessOrEq(y,x)]" ,q81);q4 Lfe;} g8-oO wB(432,"x[x==Value_t(-0.5)] " aR,m0"cAbsNotNot" ,wN);q4 Lff;qG TailCall_cLog:g5 cLog:mT(343,q21 mG,,);q4 Ldn qU gL wB(491,mU mG,mG" [fp_log(x)]" aZ,wN);q4 Lfg;} o2 wB(303,q41 mG,mV" " mG" " aH,);q4 Lfh aV(156,wQ" " mG,"[fp_log(x)]" ,wN);q4 Lfi;h8} qH TailCall_cLog10:g5 cLog10:mT(481,q21 aL,"[DO_STACKPLUS1] [fp_log10(fp_const_e())]" wH,);incStackPtr();--mStackPtr;q4 Lfj qU gL wB(492,mU aL,aL" [fp_log10(x)]" aZ,wN);q4 Lfk;} o2 wB(305,q41 aL,mV" " aL" " aH,);q4 Lfl aV(157,wQ" " aL,"[fp_log10(x)]" ,wN);q4 Lfm;h8} qH TailCall_cLog2:g5 cLog2:mT(480,q21 aN,"[DO_STACKPLUS1] [fp_log2(fp_const_e())]" wH,);incStackPtr();--mStackPtr;q4 Lfn qU cExp2:wB(344,"cExp2 " aN,,);q4 Ldn qU gL wB(490,mU aN,aN" [fp_log2(x)]" aZ,wN);q4 Lfo;} o2 wB(304,q41 aN,mV" " aN" " aH,);q4 Lfp aV(158,wQ" " aN,"[fp_log2(x)]" ,wN);q4 Lfq;h8} qH TailCall_cMax:g5 cMax hH wB(60,mX mB,,);q4 Ldn gY m9(141,mA mB,"[fp_max(x,y)]" ,q81);q4 Lga;} gP cDup:hD wB(66,aK mQ a3 mB,"B" mQ,aI(A)q91(B)wM);q4 oN cMax:hD wB(68,aK" " mB a3 mB,"B " mB,aI(A)q91(B)wM);q4 Ldn;h8} qG TailCall_cMin:g5 cMin hH wB(59,mX mC,,);q4 Ldn gY m9(140,mA mC,"[fp_min(x,y)]" ,q81);q4 Lgb;} gP cDup:hD wB(65,aK mQ a3 mC,"B" mQ,aI(A)q91(B)wM);q4 oN cMin:hD wB(67,aK" " mC a3 mC,"B " mC,aI(A)q91(B)wM);q4 Ldn;h8} qG TailCall_cMod:g5 cMod:qY if hF)){m9(104,aY a4"cMod" ,"[fp_mod(y,x)]" ,q81);q4 Lgc;} qG TailCall_cMul:g5 h3:qS cCsc:A=qK w1 3 gA]==cCos){B=hQ wB(508,aK" " aO" A[IsVarOpcode(A)&&mData->mByteCode.size()>3] cCsc" wH,"B cCot" ,aI(A)q91(B)wM);q4 Lgd;} } } q4 dY qU cDup:wB(202,"cDup" wH,"cSqr" ,);q4 Lge qU cInv:wB(214,aU wH,"cDiv" ,);q4 Lgf oF qQ h9 cDup:wB(467,"cDup" aA wH,"cSqr" aA,);q4 Lgg;oH qK qO A)gA oM B=hQ wB(473,aK wH a3 qC1 wH,m5 wH aA,aI(A)q91(B)wM);q4 Lgh;} } } } q4 dY qU cPow gM if gF h1 wB(314,mX m8 wH,"[x+Value_t(1)] cPow" ,wN);q4 Lgi;} } q4 dY gY g8 gQ h3:A=hE w0 wB(93,wS" " wZ,wX,qB1(A)wM);q4 Lgj;} q4 Default3;g7 Default3:;A=qK qR IsBinaryOpcode(A)g2 h2 qQ hE qV q6:mW(92,aY wD,wX,qB1(A)<<"," a1);q4 Lgk;g7 B g4 IsBinaryOpcode(B)g2 B)){qQ oC qV q6:mW(96,aY wK,mK,qB1(A)q91(B)<<"," a1);q4 Lgl;g7 C=oC qO C)){wB(94,"C[IsVarOpcode(C)] " wK,mK,qB1(A)q91(B)<<", C" wY(C)wM);q4 Lgm;} if(gV C)g2 C)){wB(95,"C[IsUnaryOpcode(C)&&!HasInvalidRangesOpcode(C)] " wK,"B " mK,qB1(A)q91(B)<<", C" wY(C)wM);q4 Lgn;} } } if(d1 B)){wB(90,aX wD,wX,qB1(A)q91(B)wM);q4 Lgj;} if(gV B)g2 B)){wB(91,"B[IsUnaryOpcode(B)&&!HasInvalidRangesOpcode(B)] " wD,mK,qB1(A)q91(B)wM);q4 Lgo;} } } if(d1 h2 wB(88,a5" " wZ,"[x]" ,qB1(A)wM);q4 Lgp;} if(gV A)g2 h2 wB(89,"A[IsUnaryOpcode(A)&&!HasInvalidRangesOpcode(A)] " wZ,wX,qB1(A)wM);q4 Lgq;} } } qQ h9 hS:qQ hE qV cDup d4 x+oU wB(316,"cDup[x+x==Value_t(1)]" aZ a7,,wN);q4 Lha;} wB(317,aH a7,"[x+x]" wH,wN);q4 Lhb qU o5 3 qZ hO A=qL 4]w0 wB(386,a5" y" wH aZ a7,wX" A " m3 aZ,wA", " aY"= " <()]" wH a7,"cDeg" ,q81);q4 Ldd;} if((y*x)==fp_const_deg_to_rad h7 wB(308,"y[(y*x)==fp_const_deg_to_rad()]" wH a7,"cRad" ,q81);q4 Lde;} wB(128,"y" wH a7,m3,q81);q4 Lhl;qT hI wB(122,qC1 a7,mI,wN);q4 Lhm qU cRDiv:qQ hE qV o5 3 qZ mW(285,"y" wH a9 a7,m3 a9,q81);q4 Lhn;qT hI wB(286,qC1 a9 a7,mI a9,wN);q4 Lho qU q6:mW(284,"y" a9 a7,"[y*x]" a9,q81);q4 Lhp;qT cRad:wB(210,"cRad" a7,"[DegreesToRadians(x)]" wH,wN);q4 Lhq qU cSub hL oM if(qL 3 qZ hO A=qL 4]w0 wB(387,a5" y" wH aW a7,wX" A " m3 aW,wA", " aY"= " <()]" wH,"cDeg" ,wN);q4 Lie;} if(x==fp_const_deg_to_rad h7 wB(208,"x[x==fp_const_deg_to_rad()]" wH,"cRad" ,wN);q4 Lif;h8 g7 dY:;A=dF qO A gQ cDiv:hC wB(274,aX"cDiv " wS,"[DO_STACKPLUS1] A" wH qH1,aI(A)q91(B)wM);incStackPtr();--mStackPtr;q4 Lig;} q4 d5 h3:qQ hE qV hI B=hQ wB(470,aK aA wH" " wS,m5 wH aA,aI(A)q91(B)wM);q4 Lgh;} q4 dZ;g7 dZ:;hD wB(461,aK wH" " wS,m5 wH,aI(A)q91(B)wM);q4 Lih;} } q4 d5 hI hD wB(464,aK aA" " wS,m5 aA,aI(A)q91(B)wM);q4 Lgg;} q4 d5 cRDiv hL qZ qC wB(267,"x" a9" " wS,"[DO_STACKPLUS1] " mK a9,aI(A)qD1 wM);incStackPtr();--mStackPtr;q4 Lii;} wB(281,"cRDiv " wS,"[DO_STACKPLUS1] A" wH a9,aI(A)wM);incStackPtr();--mStackPtr;q4 Lij;g7 Default4:;B=qK qR w4 wB(458,aK" " wS,m5,aI(A)q91(B)wM);q4 Lge;} } } if(gV h2 B=qK qO B qP 1 gA oM C=oC qR C==A){D=qL 4]qR D==B){wB(477,"D[D==B] C[C==A]" wH" B[IsVarOpcode(B)&&mData->mByteCode.size()>1] A[IsUnaryOpcode(A)]" wH,"D C cSqr" wH,aI(A)q91(B)<<", C" wY(C)<<", D" wY(D)wM);q4 Lik;} } } } qG TailCall_cNEqual:g5 cNEqual:oL hU wB(360,m1 wW,"[x] " wW,wN);q4 Lil qU cSqr:wB(362,q41 wL wW,"[x] " wW,wN);q4 Lil;} } m9(116,mA wW,"[fp_nequal(y,x)]" ,q81);q4 Lim;qG TailCall_cNeg:g5 hI qS h3 gM wB(123,"x" wH aA,mI,wN);q4 Lin;qT hI wB(61,qC1 aA,,);q4 Ldn qU cSin:g9 wB(244,"x" wH" " mP aA,mI" " mP,wN);q4 Lio;} qT oQ g9 wB(245,"x" wH" cSinh" aA,mI" cSinh" ,wN);q4 Lip;} qT cTan:g9 wB(246,"x" wH" " mR aA,mI" " mR,wN);q4 Liq;} qT cTanh:g9 wB(247,"x" wH" cTanh" aA,mI" cTanh" ,wN);q4 Lja;} qT hM wB(100,"x" aA,"[-x]" ,wN);q4 Ljb;} qH TailCall_cNot:g5 cNot:qS cAbs:wB(227,mV a0,"cNot" ,);q4 Ljc qU cAbsNot:A=dD wB(389,"A[IsLogicalOpcode(A)] " aS a0,"A" ,aI(A)wM);q4 Ldn;} if(A!=q6){wB(390,"A[A!=cImmed] " aS a0,"A cAbsNotNot" ,aI(A)wM);q4 Ljd;} q4 o0 qU cAbsNotNot:wB(231,"cAbsNotNot" a0,aS,);q4 Lje qU w8:wB(220,aE a0,wW,);q4 Ljf qU o1:wB(218,m2 a0,aR,);q4 Ljg qU dK:wB(219,aG a0,mJ,);q4 Ljh qU cLess:wB(216,mJ a0,aG,);q4 Lji qU cLessOrEq:wB(217,aR a0,m2,);q4 Ljj qU cNEqual:wB(221,wW a0,aE,);q4 Ljk oF wB(226,qC1 a0,"cNot" ,);q4 Ljc qU cNot:wB(229,"cNot" a0,aJ,);q4 Lbd qU dS:wB(230,aJ a0,"cNot" ,);q4 Ljc gY wB(107,"x" a0,"[fp_not(x)]" ,wN);q4 Ljl;g7 o0:;A=dF qR qX wB(391,wJ"cNot" ,"A " aS,aI(A)wM);q4 Ljm;qG TailCall_cNotNot:g5 dS d4 dF==cNot){wB(232,"cNot " aJ,"cNot" ,);gJ} qH TailCall_cOr:g5 cOr hH wB(223,mX"cOr" ,aJ,);q4 w7 m9(118,mA"cOr" ,"[fp_or(x,y)]" ,q81);q4 Ljn;h8} qH TailCall_cPow:g5 cPow:qY if(!h5 x+x)){oY cSqr){wB(22,q41"x[!isEvenInteger(x+x)] cPow" ,mV" [x+x] cPow" ,wN);q4 Ljo;} } if(isInteger(x gQ w2 wB(43,q21 wT,wX mL,wN);q4 Ljp qU cExp2:wB(44,"cExp2 " wT,wX q31,wN);q4 Ljq qU cPow hL qZ hP!isInteger(y)){wB(42,"y[!isInteger(y)] " q61 wT,aP,q81);q4 Lka;} } wB(45,q61 wT,wX" cPow" ,wN);q4 Lkb;} } if(h5 x hU wB(434,mV" x[isEvenInteger(x)] cPow" ,"[x] cPow" ,wN);q4 Lkc qU h3 hL]==cAbs){wB(435,mV wH" x[isEvenInteger(x)] cPow" ,"cMul [x] cPow" ,wN);q4 Lkd;h8} } g8)){wB(83,"x[x==Value_t()] cPow" ,"[Value_t()]" wH" [Value_t(1)]" aZ,wN);q4 Lke;} g8 oO wB(332,"x[x==Value_t(0.5)] cPow" ,q51,wN);q4 Lkf;} g8 1)/g1 3)){wB(333,"x[x==Value_t(1)/Value_t(3)] cPow" ,"cCbrt" ,wN);q4 Lkg;} g8 1)/g1-3)){wB(334,"x[x==Value_t(1)/Value_t(-3)] cPow" ,"cCbrt " aU,wN);q4 Lkh;} g8-oO wB(335,"x[x==Value_t(-0.5)] cPow" ,"cRSqrt" ,wN);q4 Lki;} g8-oG wB(336,"x[x==Value_t(-1)] cPow" ,aU,wN);q4 Lkj;} qQ h9 cPow hL qZ hP h5 y)&&!h5 x*y)){wB(21,"y[isEvenInteger(y)&&!isEvenInteger(x*y)] " q61 m8,mV" " aP,q81);q4 Lkk;} wB(330,aY q61 m8,aP,q81);q4 Lka;o2 wB(46,q41 m8,"[x+x] cPow" ,wN);q4 Lkl qU q6:hP y!=oP||x>=oP){wB(165,"y[y!=Value_t(0)||x>=Value_t(0)] " m8,"[fp_pow(y,x)]" ,q81);q4 Lkm;h8} wB(455,m8,"[DO_POWI]" ,wN)qR TryCompilePowi(x))gJ} qH TailCall_cRDiv:g5 cRDiv:qS cSinCos:wB(503,"cSinCos" a9,"cCot" ,);q4 Lep qU cSinhCosh:wB(510,"cSinhCosh" a9,"cTanh " aU,);q4 Lkn gY g8 oG wB(268,wO"cRDiv" ,aU,wN);q4 Lkj;h8} qH TailCall_cRSub:g5 cRSub d4 q0[0 h1 wB(77,"cDup" mE,"[Value_t()]" wH,);q4 Lko;} qH TailCall_cRad:g5 cRad:qS h3 gM wB(211,"x" wH" cRad" ,"[DegreesToRadians(x)]" wH,wN);q4 Lkp;qT hM wB(134,"x cRad" ,"[DegreesToRadians(x)]" ,wN);q4 Lkq;} qH TailCall_cSec:g5 cSec:A=qN qQ h9 cCos:hD wB(497,aK" " aO" " wI"cSec" ,"B " aO" " aT,aI(A)q91(B)wM);q4 Lbp;qT cSin:hD wB(495,aK" " mP" " wI"cSec" ,"B cSinCos " aU,aI(A)q91(B)wM);q4 Lla;h8 qG TailCall_cSin:g5 cSin:qS hI wB(240,m0 mP,mP aA,);q4 Llb gY wB(159,"x " mP,"[fp_sin(x)]" ,wN);q4 Llc;oH qN oY cCsc q11(499,aK" cCsc " wI mP,"B cCsc " aT,aI(A)q91(B)wM);q4 Lbp;} } qG TailCall_cSinh:g5 oQ qS cAcosh:wB(437,"cAcosh cSinh" ,"[DO_STACKPLUS1] " q41"[Value_t(-1)] " aQ,);incStackPtr();--mStackPtr;q4 Lld qU cAsinh:wB(349,"cAsinh cSinh" ,,);q4 Ldn oF wB(241,m0"cSinh" ,"cSinh" aA,);q4 Lle gY wB(160,"x cSinh" ,"[fp_sinh(x)]" ,wN);q4 Llf;} qH TailCall_cSqr:g5 cSqr:qS cAbs:wB(204,mV" cSqr" ,"cSqr" ,);q4 Llg oF wB(203,m0"cSqr" ,"cSqr" ,);q4 Llg qU cSqrt:A=dE wB(338,wJ q51" cSqr" ,"A" ,aI(A)wM);q4 Ldn;h8} qH TailCall_cSqrt:g5 cSqrt:qS hS d4 qK o3 A=hE w0 if(oC o3 wB(512,"cSqr" a3 q41 aQ,"A cHypot" ,aI(A)wM);q4 Llh;} } B g4 gV B)){A=oC w0 if(qL 4]o3 wB(513,"cSqr" a3"B[IsUnaryOpcode(B)] " q41 aQ,"A B cHypot" ," with" a8(B)qE1(A)wM);q4 Lli;} } } o2 wB(23,q41 q51,mV,);q4 Llj gY if(x>=oP){wB(161,"x[x>=Value_t(0)] " q51,"[fp_sqrt(x)]" ,wN);q4 Llk;h8} qH TailCall_cSub:g5 cSub hH wB(76,"cDup" aW,"[Value_t()]" wH,);q4 Lko oF wB(200,qC1 aW,"cAdd" ,);q4 Lll gY g8)){wB(58,"x[x==Value_t()]" aW,,wN);q4 Lba;} m9(106,aY"x" aW,"[y-x]" ,q81);q4 Llm;} wB(51,"x" aW,"[-x]" aZ,wN);q4 Lln gR w0 oY cRSub dV wB(289,"x" mE a3"cSub" ,"A" aZ" [x]" mE,aI(A)qD1 wM);q4 Llo;} wB(296,a6 a3"cSub" ,"[DO_STACKPLUS1] A" aW mE,aI(A)wM);incStackPtr();--mStackPtr;q4 Llp;} qG TailCall_cTan:g5 cTan:qS cAtan2:wB(354,"cAtan2 " mR,"cDiv" ,);q4 Lgf oF wB(242,m0 mR,mR aA,);q4 Llq gY wB(163,"x " mR,"[fp_tan(x)]" ,wN);q4 Lma;oH qN oY cCot q11(501,aK" cCot " wI mR,"B cCot " aT,aI(A)q91(B)wM);q4 Lbp;} } qG TailCall_cTanh:g5 cTanh:qS hI wB(243,m0"cTanh" ,"cTanh" aA,);q4 Lmb gY wB(164,"x cTanh" ,"[fp_tanh(x)]" ,wN);q4 Lmc;} qH TailCall_cTrunc:g5 cTrunc:qS hM wB(138,"x cTrunc" ,"[fp_trunc(x)]" ,wN);q4 Lmd gS wB(394,"A[IsAlwaysIntegerOpcode(A)] cTrunc" ,"A" ,aI(A)wM);gJ qG g7 Default0:;A=w5 qR IsComparisonOpcode(h2 qY hK qZ mW(364,aY"cAdd" wF,"[x-y] A" ,aI(A)qD1<<"," a1);q4 Lme;qT cAtan d4 dP()*g1 oO wB(380,"cAtan[fp_abs(x)()*Value_t(0.5)]" wF,"[fp_tan(x)] A" ,aI(A)qD1 wM);q4 Lmf;qT cExp d4 dO wB(370,"cExp[x>Value_t(0)]" wF,"[fp_log(x)] A" ,aI(A)qD1 wM);q4 Lmg;qT cExp2 d4 dO wB(371,"cExp2[x>Value_t(0)]" wF,"[fp_log2(x)] A" ,aI(A)qD1 wM);q4 Lmh;qT cLog:g3 wB(373,wP mG wF,"B [fp_exp(x)] A" ,aI(A)qD1 q91(B)wM);q4 Lmi;qT cLog10:g3 wB(375,wP aL wF,"B [fp_pow(Value_t(10),x)] A" ,aI(A)qD1 q91(B)wM);q4 Lmj;qT cLog2:g3 wB(374,wP aN wF,"B [fp_exp2(x)] A" ,aI(A)qD1 q91(B)wM);q4 Lmk;qT h3 hL qZ hP y>oP){wB(366,"y[y>Value_t(0)]" wH wF,"[x/y] A" ,aI(A)qD1<<"," a1);q4 Lml;} if(yoP gA qZ hP y>oP){wB(368,"y[y>Value_t(0)] cPow[x>Value_t(0)]" wF,"[fp_pow(x,Value_t(1)/y)] A" ,aI(A)qD1<<"," a1);q4 Lmo;} } qT oQ wB(381,"cSinh" wF,"[fp_asinh(x)] A" ,aI(A)qD1 wM);q4 Lmp qU cSqr d4 dO wB(369,"cSqr[x>Value_t(0)]" wF,mV" [fp_sqrt(x)] A" ,aI(A)qD1 wM);q4 Lmq;qT cTanh d4 dPmByteCode.size()>0]" ,"B" mQ,aI(A)q91(B)wM);q4 Lnb;} } if(gV h2 B=dF qO B qP 1){C=qK qR C==A){D g4 D==B){wB(476,"D[D==B] C[C==A] B[IsVarOpcode(B)&&mData->mByteCode.size()>1] A[IsUnaryOpcode(A)]" ,"D C" mQ,aI(A)q91(B)<<", C" wY(C)<<", D" wY(D)wM);q4 Lnc;} } } } C=w5 qR IsCommutativeOrParamSwappableBinaryOpcode(C)){qS cSin:A=qK w1 3 gA]==cCos){B=hQ wB(505,aK" " aO" A[IsVarOpcode(A)&&mData->mByteCode.size()>3] " mP" C[IsCommutativeOrParamSwappableBinaryOpcode(C)]" ,"B cSinCos {GetParamSwappedBinaryOpcode(C)}" ," with C" wY(C)qE1(A)q91(B)wM);q4 Lnd;} } qT oQ A=qK w1 3 gA]==cCosh){B=hQ wB(506,aK" " aM" A[IsVarOpcode(A)&&mData->mByteCode.size()>3] cSinh C[IsCommutativeOrParamSwappableBinaryOpcode(C)]" ,"B cSinhCosh {GetParamSwappedBinaryOpcode(C)}" ," with C" wY(C)qE1(A)q91(B)wM);q4 Lne;} } h8} } } q4 Laa;Laa:qW w5);gJ Lab:g6 Lnf:wE(cAbs);q4 TailCall_cAbs;Lac:q7=dP;gJ Lad:q7=fp_acos m6 Lae:q7=fp_acosh m6 Laf:oZ 4));gG Lng:w5=h3;Lnh:g0 Lni:wE(cMul);q4 TailCall_cMul;Lag:hV 4 dT oZ 4));Lnj:qW q6 hA Lah:q7=x+g1 1);gG Lfa:w5=h3;q4 Lni;Lai:gU cSub;Lnk:wE(cSub);q4 TailCall_cSub;Laj:hW 2 gH Lnl:g0 Lnm:wE(cAdd);q4 TailCall_cAdd;Lak:hW oR Lnn:qE hS);Lno:w5=cRSub;g0 wE(cRSub);q4 TailCall_cRSub;Lal:o9;qL 2 gK q4 Lnn;Lam:hW 2 gH q4 Lno;Lan:hW 4 gH Lnp:qE hS);Lnq:qE B);Loa:w5=cSub;g0 q4 Lnk;Lao:o9;oC=q6 q9 oR q4 Lnp;Lap:hW oR q4 Lnq;Laq:gT y+x;Lba:qM Ldn:q5 gJ Lbb:q8 oV o7 x q71 gX Lnn;Lbc:mM A gX Lnn;Lbd:gU dS;Lob:wE(cNotNot);q4 TailCall_cNotNot;Lbe:gT fp_and(x d6 Lbf:q7=fp_asin m6 Lbg:q7=fp_asinh m6 Lbh:q7=fp_atan m6 Lbi:gT fp_atan2(gW Lbj:q7=fp_atanh m6 Lbk:q7=fp_cbrt m6 Lbl:q1 cFloor);Loc:w5=cNeg;g0 wE(cNeg);q4 TailCall_cNeg;Lbm:q7=fp_ceil m6 Lbn:g6 Lod:wE(cCos);q4 TailCall_cCos;Lbo:q7=fp_cos m6 Lbp:dF=cDup;w5=cInv;Loe:wE(cInv);q4 TailCall_cInv;Lbq:mM cSinCos);gJ Lca:g6 wE(cCosh);q4 TailCall_cCosh;Lcb:q1 cSqr o7 g1 1));Lof:qW q6 oJ hS);Log:w5=cSqrt;g0 wE(cSqrt);q4 TailCall_cSqrt;Lcc:q7=fp_cosh m6 Lcd:mM cSinhCosh);gJ Lce:q7=RadiansToDegrees m6 Lcf:q1 cSec hA Lcg:q1 cTan hA Lch:q1 cSin hA Lci:oZ));dF dJ Loh:qE dU oZ 1));Loi:qW q6);Loj:w5=hS;q4 Lnl;Lcj:q1 cNeg oJ cExp hA Lck:q1 cNeg oJ cExp2 hA Lcl:g6 q4 Lfa;Lcm:q1 cNeg oJ cPow hA Lcn:q1 cCos hA Lco:q1 cCsc hA Lcp:gU cTan;Lok:wE(cTan);q4 TailCall_cTan;Lcq:gU cTanh;Lol:wE(cTanh);q4 TailCall_cTanh;Lda:q1 cCot hA Ldb:o9;dI Lom:wE(cDiv);q4 TailCall_cDiv;Ldc:gT y/x;q4 Lba;Ldd:qF1 q8 oR Lon:w5=cDeg;g0 wE(cDeg);q4 TailCall_cDeg;Lde:qF1 q8 oR Loo:w5=cRad;g0 wE(cRad);q4 TailCall_cRad;Ldf:gT y/x;dG Lng;Ldg:q7=g1 1)/x;q4 Lfa;Ldh:mM oI Lop:g0 q4 Lom;Ldi:q8 3 gC oI qF x q71);Loq:w5=cRDiv;g0 wE(cRDiv);q4 TailCall_cRDiv;Ldj:hV 3 gC oI qE B gX Loq;Ldk:dI Lpa:wE(cEqual);q4 TailCall_cEqual;Ldl:gT fp_equal(gW Ldm:d7 cExp o7 fp_exp(x)gX Lnj;Ldo:q7=fp_exp m6 Ldp:d7 cExp2 o7 fp_exp2(x)gX Lnj;Ldq:q7=fp_exp2 m6 Lea:qF oW g1 2))q71);Lpb:qE h3 gI cExp;g0 wE(cExp);q4 TailCall_cExp;Leb:q1 cCeil gX Loc;Lec:q7=fp_floor m6 Led:gT fp_less(x d6 Lee:qM q1 cNeg);Ljm:qE cAbsNot);gJ Lef:q7=g1 0.5)/x;qK=d8 dS;g0 q4 Lob;Leg:gT fp_lessOrEq(x d6 Leh:qM Ljd:q5 Lpc:qE cAbsNotNot);gJ Lei:q7=fp_int m6 Lej:gU cSec;wE(cSec);q4 TailCall_cSec;Lek:gU cSin;Lpd:wE(cSin);q4 TailCall_cSin;Lel:q1 cNeg gI cPow;Lpe:g0 Lpf:wE(cPow);q4 TailCall_cPow;Lem:gU cCos;q4 Lod;Len:gU cCsc;wE(cCsc);q4 TailCall_cCsc;Leo:q1 cRSqrt);gJ Lep:g6 Lpg:w5=cCot;wE(cCot);q4 TailCall_cCot;Leq:q7=g1 1)/x;gJ Lfb:q7=g1 0.5)/x;qK=d8 cNot;g0 Lph:wE(cNot);q4 TailCall_cNot;Lfc:gT fp_less(gW Lfd:qM Lje:w3 Ljm;Lfe:gT fp_lessOrEq(gW Lff:qM q1 cNeg gX Lpc;Lfg:d7 cLog o7 oW x)o8 Lfh:q1 dQ qE cLog);Lpi:qW cDup gX Loj;Lfi:q7=oW x);gJ Lfj:qF dR fp_const_e()));Lpj:dF dJ q4 Lng;Lfk:d7 cLog10 o7 dR x)o8 Lfl:q1 dQ qE cLog10 gX Lpi;Lfm:q7=dR x);gJ Lfn:qF o4 fp_const_e())gX Lpj;Lfo:d7 cLog2 o7 o4 x)o8 Lfp:q1 dQ qE cLog2 gX Lpi;Lfq:q7=o4 x);gJ Lga:gT fp_max(x d6 Lgb:gT fp_min(x d6 Lgc:gT fp_mod(gW Lgd:hV oR q0-=3;q4 Lpg;Lge:gU cSqr;Lpk:wE(cSqr);q4 TailCall_cSqr;Lgf:gU cDiv;q4 Lom;Lgg:mM cSqr gX Loc;Lgh:hV 3 gC cSqr);dM Loc;Lgi:q7=x+g1 1);gG w5=cPow;q4 Lpf;Lgj:gG q4 Lni;Lgk:gT x;Lpl:dG Lnh;Lgl:qF1 qM Lpm:hV 4 gH Lpn:o6 x);Lpo:qW q6 gX Lnh;Lgm:qM q4 Lpm;Lgn:q8 4 gC B gX Lpn;Lgo:q8 oR q4 Lpn;Lgp:qK dJ q4 Ldn;Lgq:dI q4 Lni;Lha:qM Lpp:hV oR gJ Lhb:q7=x+x;q4 Lgj;Lhc:gT x;qL 4]dJ q8 4 dT o6 y*x q71);dM Loj;Lhd:gT x;d7 dU qF y*x o8 Lhe:q7=RadiansToDegrees(x gX Lgq;Lhf:qG1 q8 4 gH Lpq:qE dU Lqa:qE B gI cDiv;q4 Lop;Lhg:o9;oC=q6 q9 oR q4 Lpq;Lhh:qG1 q8 oR q4 Lqa;Lhi:q8 4 gH q4 Lnh;Lhj:q8 4 dT qF x+x gX Lpo;Lhk:qF1 qM q4 Lpp;Lhl:qG1 q4 Lpl;Lhm:o9;q4 Lgq;Lhn:qG1 q8 oR Lqb:dM Loq;Lho:o9;qL 2 gK q4 Lqb;Lhp:qG1 dG Loq;Lhq:q7=h4 gX Lgq;Lia:gT x;qL 4]dJ q8 4 dT o6 y*x q71);dM Loa;Lib:qG1 q4 Lba;Lic:qM w3 Loc;Lid:dF=cDup;dW-=1;qM Lqc:w5=hS;q4 Lnm;Lie:qM w3 Lon;Lif:qM w3 Loo;Lig:hV oV gX Lpq;Lih:hV 2 gH Lqd:qE cSqr gX Lnh;Lii:q8 oV o7 x q71 gX Lqb;Lij:mM A gX Lqb;Lik:hV oR q4 Lqd;Lil:dI Lqe:wE(cNEqual);q4 TailCall_cNEqual;Lim:gT fp_nequal(gW Lin:o9;q4 Lcl;Lio:o9 gB cSin;g0 q4 Lpd;Lip:o9 gB cSinh;g0 wE(cSinh);q4 TailCall_cSinh;Liq:o9 gB cTan;g0 q4 Lok;Lja:o9 gB cTanh;g0 q4 Lol;Ljb:o9;gJ Ljc:g6 q4 Lph;Ljf:gU cNEqual;q4 Lqe;Ljg:gU cLessOrEq;wE(cLessOrEq);q4 TailCall_cLessOrEq;Ljh:gU cLess;wE(cLess);q4 TailCall_cLess;Lji:gU dK;wE(cGreaterOrEq);q4 TailCall_cGreaterOrEq;Ljj:gU o1;wE(cGreater);q4 TailCall_cGreater;Ljk:gU w8;q4 Lpa;Ljl:q7=fp_not m6 Ljn:gT fp_or(x d6 Ljo:d7 dQ qF x+x);Lqf:qW q6 gX Lpe;Ljp:dL Lpb;Ljq:qK=d8 cExp2;g0 wE(cExp2);q4 TailCall_cExp2;Lka:qG1 dG Lpe;Lkb:qK dJ q1 h3 gX Lpe;Lkc:dI q4 Lpf;Lkd:q8 3 dT qF x gX Lqf;Lke:q7=g1 gX Loh;Lkf:qM w3 Log;Lkg:qM q5 w5=cCbrt;g0 wE(cCbrt);q4 TailCall_cCbrt;Lkh:qM q1 cCbrt);Lqg:w5=cInv;g0 q4 Loe;Lki:qM q4 Leo;Lkj:qM w3 Lqg;Lkk:qF1 q8 3 gC dQ qF y*x gX Lqf;Lkl:q7=x+x;q4 Lkc;Lkm:gT oX gW Lkn:q1 cTanh gX Lqg;Lko:oZ)gX Lpj;Lkp:q7=h4 gX Lcl;Lkq:q7=h4);gJ Lla:mM cSinCos gX Lqg;Llb:q1 cSin gX Loc;Llc:q7=fp_sin m6 Lld:q1 cSqr o7 g1-1)gX Lof;Lle:q1 cSinh gX Loc;Llf:q7=fp_sinh m6 Llg:g6 q4 Lpk;Llh:hV 4 gC A);Lqh:w5=cHypot;g0 wE(cHypot);q4 TailCall_cHypot;Lli:hV 5 gC A oJ B gX Lqh;Llj:gU cAbs;q4 Lnf;Llk:q7=fp_sqrt m6 Lll:g6 q4 Lqc;Llm:gT y-x;q4 Lba;Lln:o9;q4 Lqc;Llo:q8 oV oJ hS o7 x q71 gX Lno;Llp:mM A oJ cSub gX Lno;Llq:q1 cTan gX Loc;Lma:q7=fp_tan m6 Lmb:q1 cTanh gX Loc;Lmc:q7=fp_tanh m6 Lmd:q7=fp_trunc m6 Lme:gT x-y;Lqi:q8 2 gH Lqj:qE A);gJ Lmf:q7=fp_tan(x);Lqk:dL Lqj;Lmg:q7=oW x gX Lqk;Lmh:q7=o4 x gX Lqk;Lmi:q7=fp_exp(x gX Lqk;Lmj:q7=oX g1 10),x gX Lqk;Lmk:q7=fp_exp2(x gX Lqk;Lml:gT x/y;q4 Lqi;Lmm:gT x/y;q8 2 gH Lql:qE OppositeComparisonOpcode(A));gJ Lmn:o9;dL Lql;Lmo:gT oX x,g1 1)/y gX Lqi;Lmp:q7=fp_asinh(x gX Lqk;Lmq:d7 dQ qF fp_sqrt(x)q71 gX Lqj;Lna:q7=fp_atanh(x gX Lqk;Lnb:qW cDup);gJ Lnc:dF=cDup;gJ Lnd:hV 3 gC cSinCos);Lqm:qE GetParamSwappedBinaryOpcode(C));gJ Lne:hV 3 gC cSinhCosh gX Lqm;gJ q4 TailCall_cAcos;q4 TailCall_cAcosh;q4 TailCall_cAnd;q4 TailCall_cAsin;q4 TailCall_cAsinh;q4 TailCall_cAtan;q4 TailCall_cAtan2;q4 TailCall_cAtanh;q4 TailCall_cCeil;q4 TailCall_cFloor;q4 TailCall_cInt;q4 TailCall_cLog;q4 TailCall_cLog10;q4 TailCall_cLog2;q4 TailCall_cMax;q4 TailCall_cMin;q4 TailCall_cMod;q4 TailCall_cOr;q4 TailCall_cRDiv;q4 TailCall_cRad;q4 TailCall_cSec;q4 TailCall_cSin;q4 TailCall_cSinh;q4 TailCall_cSqrt;q4 TailCall_cSub;q4 TailCall_cTan;q4 TailCall_cTanh;q4 TailCall_cTrunc; #endif #if((FP_COMPLEX_VERSION) && (FP_FLOAT_VERSION)) dH x;dH gE A;hT C;hT D;qQ w5){TailCall_cAbs:g5 cAbs:qS h0} qH TailCall_cAcos:g5 cAcos:qY wB(172,"x cAcos" ,"[fp_acos(x)]" ,wN);q4 Lad;} qH TailCall_cAcosh:g5 cAcosh:qY wB(169,"x cAcosh" ,"[fp_acosh(x)]" ,wN);q4 Lae;} qH TailCall_cAdd:g5 hG Laf;qT h3 hL]==hS){if(qL gZ Lag;} h8} q4 dX qU d2 gF h1 wB(313,"cDup" a7 aZ,"[x+Value_t(1)]" wH,wN);q4 Lah;} } q4 dX oF wB(199,qC1 aZ,"cSub" ,);q4 Lai gY hK qZ mW(127,aY"cAdd" mD,"[y+x]" aZ,q81);q4 Laj;qT cRSub:qQ hE d3 3 qZ mW(298,aY"cAdd" mE mD,mN aZ mE,q81);q4 Lak;qT hI wB(299,m0 a6 mD,"[-x]" aZ mE,wN);q4 Lal qU q6:mW(297,aY a6 mD,mN mE,q81);q4 Lam;qT oA Lan;qT hI wB(293,m0"B[IsVarOpcode(B)]" aW mD,"[-x]" aZ" B" aW,wA"," a8(B)wM);q4 Lao qU q6:mW(291,aY"B[IsVarOpcode(B)]" aW mD,mN" B" aW,wA"," a8(B)<<"," a1);q4 Lap;} w9 mW(105,aY aF,"[y+x]" ,q81);q4 Laq;} g8)){wB(57,"x[x==Value_t()]" aZ,,wN);q4 Lba;h8 g7 dX:;A=dF w0 oY cRSub dV wB(290,"x" mE a3"cAdd" ,"[DO_STACKPLUS1] A [x]" aZ mE,aI(A)qD1 wM);incStackPtr();--mStackPtr;q4 Lbb;} wB(295,a6 a3"cAdd" ,"[DO_STACKPLUS1] A" aZ mE,aI(A)wM);incStackPtr();--mStackPtr;q4 Lbc;} qG TailCall_cAnd:g5 cAnd hH wB(224,mX"cAnd" ,aJ,);q4 w7 m9(117,mA"cAnd" ,"[fp_and(x,y)]" ,q81);q4 Lbe;h8} qH TailCall_cArg:g5 cArg:qY wB(190,"x cArg" ,"[fp_arg(x)]" ,wN);q4 Lbf;} qH TailCall_cAsin:g5 cAsin:qY wB(173,"x cAsin" ,"[fp_asin(x)]" ,wN);q4 Lbg;} qH TailCall_cAsinh:g5 cAsinh:qY wB(170,"x cAsinh" ,"[fp_asinh(x)]" ,wN);q4 Lbh;} qH TailCall_cAtan:g5 cAtan:qY if(g1 x.real(),fp_abs(x.imag()))!=g1 0,oG wB(174,"x[Value_t(x.real(),fp_abs(x.imag()))!=Value_t(0,1)] cAtan" ,"[fp_atan(x)]" ,wN);q4 Lbi;qG TailCall_cAtan2:g5 cAtan2:qY m9(139,mA"cAtan2" ,"[fp_atan2(y,x)]" ,q81);q4 Lbj;qG TailCall_cAtanh:g5 cAtanh:qY if(g1 fp_abs(x.real()),x.imag())!=g1 1,0)){wB(171,"x[Value_t(fp_abs(x.real()),x.imag())!=Value_t(1,0)] cAtanh" ,"[fp_atanh(x)]" ,wN);q4 Lbk;qG TailCall_cCbrt:g5 cCbrt:qY wB(175,"x cCbrt" ,"[fp_cbrt(x)]" ,wN);q4 Lbl;} qH TailCall_cCeil:g5 cCeil:qS hI wB(402,m0 q01,mS aA,);q4 Lbm gY wB(135,"x " q01,"[fp_ceil(x)]" ,wN);q4 Lbn gS wB(396,"A[IsAlwaysIntegerOpcode(A)] " q01,"A" ,aI(A)wM);gJ qG TailCall_cConj:g5 cConj:qS cConj:wB(63,mY" " mY,,);oS gY wB(193,"x " mY,"[fp_conj(x)]" ,wN);q4 Lbp;} qH TailCall_cCos:g5 cCos:qS cAcos:wB(346,"cAcos " aO,,);q4 oE wB(238,m0 aO,aO,);q4 Lbq gY wB(176,"x " aO,"[fp_cos(x)]" ,wN);q4 Lca;oH qN qQ h9 cSec:hD wB(500,aK" cSec " wI aO,"B cSec " aT,aI(A)q91(B)wM);q4 Lcb;qT cSin:hD wB(494,aK" " mP" " wI aO,"B cSinCos" ,aI(A)q91(B)wM);q4 Lcc;h8} qG TailCall_cCosh:g5 cCosh:qS cAsinh:wB(450,"cAsinh " aM,"[DO_STACKPLUS1] " q41"[Value_t(1)] " aQ,);incStackPtr();--mStackPtr;q4 Lcd oF wB(239,m0 aM,aM,);q4 Lce gY wB(177,"x " aM,"[fp_cosh(x)]" ,wN);q4 Lcf;oH qN oY cSinh q11(507,aK" cSinh " wI aM,"B cSinhCosh" ,aI(A)q91(B)wM);q4 Lcg;} } qG TailCall_cCot:g5 cCot:A=qN oY cTan q11(498,aK" " mR" " wI"cCot" ,"B " mR" " aT,aI(A)q91(B)wM);q4 Lcb;} qG TailCall_cCsc:g5 cCsc:A=qN oY cSin q11(496,aK" " mP" " wI"cCsc" ,"B " mP" " aT,aI(A)q91(B)wM);q4 Lcb;} qG TailCall_cDeg:g5 cDeg:qY wB(133,"x cDeg" ,"[RadiansToDegrees(x)]" ,wN);q4 Lch;} qH TailCall_cDiv:g5 cDiv:qS cCos:wB(250,aO mF,"cSec" wH,);q4 Lci qU cCot:wB(254,"cCot" mF,mR wH,);q4 Lcj qU cCsc:wB(252,"cCsc" mF,mP wH,);q4 Lck qU cDup:wB(78,"cDup" mF,"[Value_t()]" wH" [Value_t(1)]" aZ,);q4 Lcl qU w2 wB(408,"cExp" mF,m0"cExp" wH,);q4 Lcm qU cExp2:wB(409,"cExp2" mF,m0"cExp2" wH,);q4 Lcn qU cInv:wB(213,aU mF,"cMul" ,);q4 Lco qU cPow:wB(407,"cPow" mF,m0"cPow" wH,);q4 Lcp qU cSec:wB(253,"cSec" mF,aO wH,);q4 Lcq qU cSin:wB(249,mP mF,"cCsc" wH,);q4 Lda qU cSinCos:wB(502,"cSinCos" mF,mR,);q4 Ldb qU cSinhCosh:wB(509,"cSinhCosh" mF,"cTanh" ,);q4 Ldc qU cTan:wB(251,mR mF,"cCot" wH,);q4 Ldd gY if hF gQ hI wB(125,m0 a4"cDiv" ,"[-x]" mF,wN);q4 Lde qU q6:mW(103,aY a4"cDiv" ,"[y/x]" ,q81);q4 Ldf;} } g8 oG wB(56,wO"cDiv" ,,wN);q4 Lba;} dB h3 gA qZ hP(y/x)==fp_const_rad_to_deg h7 wB(321,"y[(y/x)==fp_const_rad_to_deg()]" wH" " wR,"cDeg" ,q81);q4 Ldg;} if((y/x)==fp_const_deg_to_rad h7 wB(322,"y[(y/x)==fp_const_deg_to_rad()]" wH" " wR,"cRad" ,q81);q4 Ldh;} wB(323,"y" wH" " wR,"[y/x]" wH,q81);q4 Ldi;} } wB(325,wR,"[Value_t(1)/x]" wH,wN);q4 Ldj;} gP cDiv:hC wB(271,aX"cDiv " wV,"[DO_STACKPLUS1] B A" wH mF,aI(A)q91(B)wM);incStackPtr();--mStackPtr;q4 Ldk;qT cRDiv:qQ hE qV hM wB(266,"x" a9" " wV,"A" wH" [x]" a9,aI(A)qD1 wM);q4 Ldl;g7 hC wB(265,"B[IsVarOpcode(B)]" a9" " wV,"A" wH" B" a9,aI(A)q91(B)wM);q4 Ldm;} h8} qG TailCall_cEqual:g5 w8:oL hU wB(359,m1 aE,"[x] " aE,wN);q4 Ldn qU cSqr:wB(361,q41 wL aE,"[x] " aE,wN);q4 Ldn;} } m9(115,mA aE,"[fp_equal(y,x)]" ,q81);q4 Ldo;qG TailCall_cExp:g5 w2 qS hS gM wB(404,aF mL,q21"[fp_exp(x)]" wH,wN);q4 Ldp;qT cLog:A=dE wB(340,wJ mG mL,"A" ,aI(A)wM);oS;qT hM wB(178,"x" mL,"[fp_exp(x)]" ,wN);q4 Ldq;} qH TailCall_cExp2:g5 cExp2:qS hS gM wB(405,aF q31,"cExp2 [fp_exp2(x)]" wH,wN);q4 Lea;qT cLog2:A=dE wB(341,wJ aN q31,"A" ,aI(A)wM);oS;qT hM wB(179,"x" q31,"[fp_exp2(x)]" ,wN);q4 Leb;} wB(479,"cExp2" ,"[DO_STACKPLUS1] [fp_log(Value_t(2))]" wH mL,);incStackPtr();--mStackPtr;q4 Lec;TailCall_cFloor:g5 cFloor:qS hI wB(401,m0 mS,q01 aA,);q4 Led gY wB(136,"x " mS,"[fp_floor(x)]" ,wN);q4 Lee gS wB(395,"A[IsAlwaysIntegerOpcode(A)] " mS,"A" ,aI(A)wM);gJ qG TailCall_cGreater:g5 o1:qY m9(113,mA m2,"[fp_less(x,y)]" ,q81);q4 Lef;qG TailCall_cGreaterOrEq:g5 dK:qY m9(114,mA aG,"[fp_lessOrEq(x,y)]" ,q81);q4 Leg;qG TailCall_cHypot:g5 cHypot d4 dF==cSinCos){wB(84,"cSinCos cHypot" ,"[Value_t()]" wH" [Value_t(1)]" aZ,);q4 Lcl;} qH TailCall_cImag:g5 cImag:qS cAbs:wB(81,mV" " mZ,"[Value_t()]" wH,);q4 Leh qU cReal:wB(80,"cReal " mZ,"[Value_t()]" wH,);q4 Leh gY wB(192,"x " mZ,"[fp_imag(x)]" ,wN);q4 Lei;} qH TailCall_cInt:g5 cInt:qS hM wB(137,"x cInt" ,"[fp_int(x)]" ,wN);q4 Lej gS wB(397,"A[IsAlwaysIntegerOpcode(A)] cInt" ,"A" ,aI(A)wM);gJ qG TailCall_cInv:g5 cInv:qS cCos:wB(256,aO" " aU,"cSec" ,);q4 Lek qU cCot:wB(260,"cCot " aU,mR,);q4 Ldb qU cCsc:wB(258,"cCsc " aU,mP,);q4 Lel qU cInv:wB(62,aU" " aU,,);oS qU cPow:wB(355,q61 aU,m0"cPow" ,);q4 Lem qU cSec:wB(259,"cSec " aU,aO,);q4 Len qU cSin:wB(255,mP" " aU,"cCsc" ,);q4 Leo qU cSqrt:wB(206,q51" " aU,"cRSqrt" ,);q4 Lep qU cTan:wB(257,mR" " aU,"cCot" ,);q4 Leq gY if hF)){wB(101,a4 aU,"[Value_t(1)/x]" ,wN);q4 Lfa;h8} qH TailCall_cLess:g5 cLess:oL)){A=dE wB(301,wJ wL mJ,mK,qB1(A)wM);q4 Lfb;} } m9(111,mA mJ,"[fp_less(y,x)]" ,q81);q4 Lfc;qG TailCall_cLessOrEq:g5 cLessOrEq:qY m9(112,mA aR,"[fp_lessOrEq(y,x)]" ,q81);q4 Lfd;qG TailCall_cLog:g5 cLog:mT(343,q21 mG,,);oS qU gL wB(491,mU mG,mG" [fp_log(x)]" aZ,wN);q4 Lfe;} oD wB(180,qA1 mG,"[fp_log(x)]" ,wN);q4 Lff;h8} qH TailCall_cLog10:g5 cLog10:mT(481,q21 aL,"[DO_STACKPLUS1] [fp_log10(fp_const_e())]" wH,);incStackPtr();--mStackPtr;q4 Lfg qU gL wB(492,mU aL,aL" [fp_log10(x)]" aZ,wN);q4 Lfh;} oD wB(181,qA1 aL,"[fp_log10(x)]" ,wN);q4 Lfi;h8} qH TailCall_cLog2:g5 cLog2:mT(480,q21 aN,"[DO_STACKPLUS1] [fp_log2(fp_const_e())]" wH,);incStackPtr();--mStackPtr;q4 Lfj qU cExp2:wB(344,"cExp2 " aN,,);oS qU gL wB(490,mU aN,aN" [fp_log2(x)]" aZ,wN);q4 Lfk;} oD wB(182,qA1 aN,"[fp_log2(x)]" ,wN);q4 Lfl;h8} qH TailCall_cMax:g5 cMax hH wB(60,mX mB,,);oS gY m9(141,mA mB,"[fp_max(x,y)]" ,q81);q4 Lfm;} gP cDup:hD wB(66,aK mQ a3 mB,"B" mQ,aI(A)q91(B)wM);oS;qT cMax:hD wB(68,aK" " mB a3 mB,"B " mB,aI(A)q91(B)wM);oS;h8} qG TailCall_cMin:g5 cMin hH wB(59,mX mC,,);oS gY m9(140,mA mC,"[fp_min(x,y)]" ,q81);q4 Lfn;} gP cDup:hD wB(65,aK mQ a3 mC,"B" mQ,aI(A)q91(B)wM);oS;qT cMin:hD wB(67,aK" " mC a3 mC,"B " mC,aI(A)q91(B)wM);oS;h8} qG TailCall_cMod:g5 cMod:qY if hF)){m9(104,aY a4"cMod" ,"[fp_mod(y,x)]" ,q81);q4 Lfo;} qG TailCall_cMul:g5 h3:qS cCsc:A=qK w1 3 gA]==cCos){B=hQ wB(508,aK" " aO" A[IsVarOpcode(A)&&mData->mByteCode.size()>3] cCsc" wH,"B cCot" ,aI(A)q91(B)wM);q4 Lfp;} } } q4 dY qU cDup:wB(202,"cDup" wH,"cSqr" ,);q4 Lfq qU cInv:wB(214,aU wH,"cDiv" ,);q4 Lga oF qQ h9 cDup:wB(467,"cDup" aA wH,"cSqr" aA,);q4 Lgb;oH qK qO A)gA oM B=hQ wB(473,aK wH a3 qC1 wH,m5 wH aA,aI(A)q91(B)wM);q4 Lgc;} } } } q4 dY qU cPow gM if gF h1 wB(314,mX m8 wH,"[x+Value_t(1)] cPow" ,wN);q4 Lgd;} } q4 dY gY g8 gQ h3:A=hE w0 wB(93,wS" " wZ,wX,qB1(A)wM);q4 Lge;} q4 Default3;g7 Default3:;A=qK qR IsBinaryOpcode(A)g2 h2 qQ hE qV q6:mW(92,aY wD,wX,qB1(A)<<"," a1);q4 Lgf;g7 B g4 IsBinaryOpcode(B)g2 B)){qQ oC qV q6:mW(96,aY wK,mK,qB1(A)q91(B)<<"," a1);q4 Lgg;g7 C=oC qO C)){wB(94,"C[IsVarOpcode(C)] " wK,mK,qB1(A)q91(B)<<", C" wY(C)wM);q4 Lgh;} if(gV C)g2 C)){wB(95,"C[IsUnaryOpcode(C)&&!HasInvalidRangesOpcode(C)] " wK,"B " mK,qB1(A)q91(B)<<", C" wY(C)wM);q4 Lgi;} } } if(d1 B)){wB(90,aX wD,wX,qB1(A)q91(B)wM);q4 Lge;} if(gV B)g2 B)){wB(91,"B[IsUnaryOpcode(B)&&!HasInvalidRangesOpcode(B)] " wD,mK,qB1(A)q91(B)wM);q4 Lgj;} } } if(d1 h2 wB(88,a5" " wZ,"[x]" ,qB1(A)wM);q4 Lgk;} if(gV A)g2 h2 wB(89,"A[IsUnaryOpcode(A)&&!HasInvalidRangesOpcode(A)] " wZ,wX,qB1(A)wM);q4 Lgl;} } } qQ h9 hS:qQ hE qV cDup d4 x+oU wB(316,"cDup[x+x==Value_t(1)]" aZ a7,,wN);q4 Lgm;} wB(317,aH a7,"[x+x]" wH,wN);q4 Lgn qU o5 3 qZ hO A=qL 4]w0 wB(386,a5" y" wH aZ a7,wX" A " m3 aZ,wA", " aY"= " <()]" wH a7,"cDeg" ,q81);q4 Ldg;} if((y*x)==fp_const_deg_to_rad h7 wB(308,"y[(y*x)==fp_const_deg_to_rad()]" wH a7,"cRad" ,q81);q4 Ldh;} wB(128,"y" wH a7,m3,q81);q4 Lhg;qT hI wB(122,qC1 a7,mI,wN);q4 Lhh qU cRDiv:qQ hE qV o5 3 qZ mW(285,"y" wH a9 a7,m3 a9,q81);q4 Lhi;qT hI wB(286,qC1 a9 a7,mI a9,wN);q4 Lhj qU q6:mW(284,"y" a9 a7,"[y*x]" a9,q81);q4 Lhk;qT cRad:wB(210,"cRad" a7,"[DegreesToRadians(x)]" wH,wN);q4 Lhl qU cSub hL oM if(qL 3 qZ hO A=qL 4]w0 wB(387,a5" y" wH aW a7,wX" A " m3 aW,wA", " aY"= " <()]" wH,"cDeg" ,wN);q4 Lhq;} if(x==fp_const_deg_to_rad h7 wB(208,"x[x==fp_const_deg_to_rad()]" wH,"cRad" ,wN);q4 Lia;h8 g7 dY:;A=dF qO A gQ cDiv:hC wB(274,aX"cDiv " wS,"[DO_STACKPLUS1] A" wH qH1,aI(A)q91(B)wM);incStackPtr();--mStackPtr;q4 Lib;} q4 d5 h3:qQ hE qV hI B=hQ wB(470,aK aA wH" " wS,m5 wH aA,aI(A)q91(B)wM);q4 Lgc;} q4 dZ;g7 dZ:;hD wB(461,aK wH" " wS,m5 wH,aI(A)q91(B)wM);q4 Lic;} } q4 d5 hI hD wB(464,aK aA" " wS,m5 aA,aI(A)q91(B)wM);q4 Lgb;} q4 d5 cRDiv hL qZ qC wB(267,"x" a9" " wS,"[DO_STACKPLUS1] " mK a9,aI(A)qD1 wM);incStackPtr();--mStackPtr;q4 Lid;} wB(281,"cRDiv " wS,"[DO_STACKPLUS1] A" wH a9,aI(A)wM);incStackPtr();--mStackPtr;q4 Lie;g7 Default4:;B=qK qR w4 wB(458,aK" " wS,m5,aI(A)q91(B)wM);q4 Lfq;} } } if(gV h2 B=qK qO B qP 1 gA oM C=oC qR C==A){D=qL 4]qR D==B){wB(477,"D[D==B] C[C==A]" wH" B[IsVarOpcode(B)&&mData->mByteCode.size()>1] A[IsUnaryOpcode(A)]" wH,"D C cSqr" wH,aI(A)q91(B)<<", C" wY(C)<<", D" wY(D)wM);q4 Lif;} } } } qG TailCall_cNEqual:g5 cNEqual:oL hU wB(360,m1 wW,"[x] " wW,wN);q4 Lig qU cSqr:wB(362,q41 wL wW,"[x] " wW,wN);q4 Lig;} } m9(116,mA wW,"[fp_nequal(y,x)]" ,q81);q4 Lih;qG TailCall_cNeg:g5 hI qS h3 gM wB(123,"x" wH aA,mI,wN);q4 Lii;qT hI wB(61,qC1 aA,,);oS qU cSin:g9 wB(244,"x" wH" " mP aA,mI" " mP,wN);q4 Lij;} qT oQ g9 wB(245,"x" wH" cSinh" aA,mI" cSinh" ,wN);q4 Lik;} qT cTan:g9 wB(246,"x" wH" " mR aA,mI" " mR,wN);q4 Lil;} qT cTanh:g9 wB(247,"x" wH" cTanh" aA,mI" cTanh" ,wN);q4 Lim;} qT hM wB(100,"x" aA,"[-x]" ,wN);q4 Lin;} qH TailCall_cNot:g5 cNot:qS cAbsNotNot:wB(231,"cAbsNotNot" a0,aS,);q4 Lio qU w8:wB(220,aE a0,wW,);q4 Lip qU o1:wB(218,m2 a0,aR,);q4 Liq qU dK:wB(219,aG a0,mJ,);q4 Lja qU cLess:wB(216,mJ a0,aG,);q4 Ljb qU cLessOrEq:wB(217,aR a0,m2,);q4 Ljc qU cNEqual:wB(221,wW a0,aE,);q4 Ljd qU cNot:wB(229,"cNot" a0,aJ,);q4 Lbd qU dS:wB(230,aJ a0,"cNot" ,);q4 Lje gY wB(107,"x" a0,"[fp_not(x)]" ,wN);q4 Ljf;} qH TailCall_cNotNot:g5 dS d4 dF==cNot){wB(232,"cNot " aJ,"cNot" ,);gJ} qH TailCall_cOr:g5 cOr hH wB(223,mX"cOr" ,aJ,);q4 w7 m9(118,mA"cOr" ,"[fp_or(x,y)]" ,q81);q4 Ljg;h8} qH TailCall_cPolar:g5 cPolar d4 q0[0 qZ y=q7;qJ x gO wB(194,"x " aY"cPolar" ,"[fp_polar(x,y)]" ," with " aY"= " <mByteCode.size()>0]" ,"B" mQ,aI(A)q91(B)wM);q4 Lll;} } if(gV h2 B=dF qO B qP 1){C=qK qR C==A){D g4 D==B){wB(476,"D[D==B] C[C==A] B[IsVarOpcode(B)&&mData->mByteCode.size()>1] A[IsUnaryOpcode(A)]" ,"D C" mQ,aI(A)q91(B)<<", C" wY(C)<<", D" wY(D)wM);q4 Llm;} } } } C=w5 qR IsCommutativeOrParamSwappableBinaryOpcode(C)){qS cSin:A=qK w1 3 gA]==cCos){B=hQ wB(505,aK" " aO" A[IsVarOpcode(A)&&mData->mByteCode.size()>3] " mP" C[IsCommutativeOrParamSwappableBinaryOpcode(C)]" ,"B cSinCos {GetParamSwappedBinaryOpcode(C)}" ," with C" wY(C)qE1(A)q91(B)wM);q4 Lln;} } qT oQ A=qK w1 3 gA]==cCosh){B=hQ wB(506,aK" " aM" A[IsVarOpcode(A)&&mData->mByteCode.size()>3] cSinh C[IsCommutativeOrParamSwappableBinaryOpcode(C)]" ,"B cSinhCosh {GetParamSwappedBinaryOpcode(C)}" ," with C" wY(C)qE1(A)q91(B)wM);q4 Llo;} } h8} } } q4 Laa;Laa:qW w5);gJ Lab:g6 Llp:wE(cAbs);q4 TailCall_cAbs;Lac:q7=dP;gJ Lad:q7=fp_acos m6 Lae:q7=fp_acosh m6 Laf:oZ 4));gG Llq:w5=h3;Lma:g0 Lmb:wE(cMul);q4 TailCall_cMul;Lag:hV 4 dT oZ 4));Lmc:qW q6 hY;Lah:q7=x+g1 1);gG Lfb:w5=h3;q4 Lmb;Lai:gU cSub;Lmd:wE(cSub);q4 TailCall_cSub;Laj:hW 2 gH Lme:g0 Lmf:wE(cAdd);q4 TailCall_cAdd;Lak:hW oR Lmg:qE hS);Lmh:w5=cRSub;g0 wE(cRSub);q4 TailCall_cRSub;Lal:o9;qL 2 gK q4 Lmg;Lam:hW 2 gH q4 Lmh;Lan:hW 4 gH Lmi:qE hS);Lmj:qE B);Lmk:w5=cSub;g0 q4 Lmd;Lao:o9;oC=q6 q9 oR q4 Lmi;Lap:hW oR q4 Lmj;Laq:gT y+x;Lba:qM Lbo:q5 gJ Lbb:q8 oV o7 x q71 gX Lmg;Lbc:mM A gX Lmg;Lbd:gU dS;wE(cNotNot);q4 TailCall_cNotNot;Lbe:gT fp_and(x d6 Lbf:q7=fp_arg m6 Lbg:q7=fp_asin m6 Lbh:q7=fp_asinh m6 Lbi:q7=fp_atan m6 Lbj:gT fp_atan2(gW Lbk:q7=fp_atanh m6 Lbl:q7=fp_cbrt m6 Lbm:q1 cFloor);Lml:w5=cNeg;g0 wE(cNeg);q4 TailCall_cNeg;Lbn:q7=fp_ceil m6 Lbp:q7=fp_conj m6 Lbq:g6 Lmm:wE(cCos);q4 TailCall_cCos;Lca:q7=fp_cos m6 Lcb:dF=cDup;w5=cInv;Lmn:wE(cInv);q4 TailCall_cInv;Lcc:mM cSinCos);gJ Lcd:q1 cSqr o7 g1 1));Lmo:qW q6 oJ hS);Lmp:w5=cSqrt;g0 wE(cSqrt);q4 TailCall_cSqrt;Lce:g6 wE(cCosh);q4 TailCall_cCosh;Lcf:q7=fp_cosh m6 Lcg:mM cSinhCosh);gJ Lch:q7=RadiansToDegrees m6 Lci:q1 cSec hY;Lcj:q1 cTan hY;Lck:q1 cSin hY;Lcl:oZ));dF dJ Lmq:qE dU oZ 1));Lna:qW q6);Lnb:w5=hS;q4 Lme;Lcm:q1 cNeg oJ cExp hY;Lcn:q1 cNeg oJ cExp2 hY;Lco:g6 q4 Lfb;Lcp:q1 cNeg oJ cPow hY;Lcq:q1 cCos hY;Lda:q1 cCsc hY;Ldb:gU cTan;Lnc:wE(cTan);q4 TailCall_cTan;Ldc:gU cTanh;Lnd:wE(cTanh);q4 TailCall_cTanh;Ldd:q1 cCot hY;Lde:o9;dI Lne:wE(cDiv);q4 TailCall_cDiv;Ldf:gT y/x;q4 Lba;Ldg:qF1 q8 oR Lnf:w5=cDeg;g0 wE(cDeg);q4 TailCall_cDeg;Ldh:qF1 q8 oR Lng:w5=cRad;g0 wE(cRad);q4 TailCall_cRad;Ldi:gT y/x;dG Llq;Ldj:q7=g1 1)/x;q4 Lfb;Ldk:mM oI Lnh:g0 q4 Lne;Ldl:q8 3 gC oI qF x q71);Lni:w5=cRDiv;g0 wE(cRDiv);q4 TailCall_cRDiv;Ldm:hV 3 gC oI qE B gX Lni;Ldn:dI Lnj:wE(cEqual);q4 TailCall_cEqual;Ldo:gT fp_equal(gW Ldp:d7 cExp o7 fp_exp(x)gX Lmc;Ldq:q7=fp_exp m6 Lea:d7 cExp2 o7 fp_exp2(x)gX Lmc;Leb:q7=fp_exp2 m6 Lec:qF oW g1 2))q71);Lnk:qE h3 gI cExp;g0 wE(cExp);q4 TailCall_cExp;Led:q1 cCeil oT Lee:q7=fp_floor m6 Lef:gT fp_less(x d6 Leg:gT fp_lessOrEq(x d6 Leh:oZ));Lnl:dF dJ q4 Llq;Lei:q7=fp_imag m6 Lej:q7=fp_int m6 Lek:gU cSec;wE(cSec);q4 TailCall_cSec;Lel:gU cSin;Lnm:wE(cSin);q4 TailCall_cSin;Lem:q1 cNeg gI cPow;Lnn:g0 Lno:wE(cPow);q4 TailCall_cPow;Len:gU cCos;q4 Lmm;Leo:gU cCsc;wE(cCsc);q4 TailCall_cCsc;Lep:q1 cRSqrt);gJ Leq:g6 Lnp:w5=cCot;wE(cCot);q4 TailCall_cCot;Lfa:q7=g1 1)/x;gJ Lfc:gT fp_less(gW Lfd:gT fp_lessOrEq(gW Lfe:d7 cLog o7 oW x)gX Lna;Lff:q7=oW x);gJ Lfg:qF dR fp_const_e())gX Lnl;Lfh:d7 cLog10 o7 dR x)gX Lna;Lfi:q7=dR x);gJ Lfj:qF o4 fp_const_e())gX Lnl;Lfk:d7 cLog2 o7 o4 x)gX Lna;Lfl:q7=o4 x);gJ Lfm:gT fp_max(x d6 Lfn:gT fp_min(x d6 Lfo:gT fp_mod(gW Lfp:hV oR q0-=3;q4 Lnp;Lfq:gU cSqr;Lnq:wE(cSqr);q4 TailCall_cSqr;Lga:gU cDiv;q4 Lne;Lgb:mM cSqr oT Lgc:hV 3 gC cSqr);dM Lml;Lgd:q7=x+g1 1);gG w5=cPow;q4 Lno;Lge:gG q4 Lmb;Lgf:gT x;Loa:dG Lma;Lgg:qF1 qM Lob:hV 4 gH Loc:o6 x);Lod:qW q6 gX Lma;Lgh:qM q4 Lob;Lgi:q8 4 gC B gX Loc;Lgj:q8 oR q4 Loc;Lgk:qK dJ oS;Lgl:dI q4 Lmb;Lgm:qM Loe:hV oR gJ Lgn:q7=x+x;q4 Lge;Lgo:gT x;qL 4]dJ q8 4 dT o6 y*x q71);dM Lnb;Lgp:gT x;d7 dU qF y*x gX Lna;Lgq:q7=RadiansToDegrees(x gX Lgl;Lha:qG1 q8 4 gH Lof:qE dU Log:qE B gI cDiv;q4 Lnh;Lhb:o9;oC=q6 q9 oR q4 Lof;Lhc:qG1 q8 oR q4 Log;Lhd:q8 4 gH q4 Lma;Lhe:q8 4 dT qF x+x gX Lod;Lhf:qF1 qM q4 Loe;Lhg:qG1 q4 Loa;Lhh:o9;q4 Lgl;Lhi:qG1 q8 oR Loh:dM Lni;Lhj:o9;qL 2 gK q4 Loh;Lhk:qG1 dG Lni;Lhl:q7=h4 gX Lgl;Lhm:gT x;qL 4]dJ q8 4 dT o6 y*x q71);dM Lmk;Lhn:qG1 q4 Lba;Lho:qM w3 Lml;Lhp:dF=cDup;dW-=1;qM Loi:w5=hS;q4 Lmf;Lhq:qM w3 Lnf;Lia:qM w3 Lng;Lib:hV oV gX Lof;Lic:hV 2 gH Loj:qE cSqr gX Lma;Lid:q8 oV o7 x q71 gX Loh;Lie:mM A gX Loh;Lif:hV oR q4 Loj;Lig:dI Lok:wE(cNEqual);q4 TailCall_cNEqual;Lih:gT fp_nequal(gW Lii:o9;q4 Lco;Lij:o9 gB cSin;g0 q4 Lnm;Lik:o9 gB cSinh;g0 wE(cSinh);q4 TailCall_cSinh;Lil:o9 gB cTan;g0 q4 Lnc;Lim:o9 gB cTanh;g0 q4 Lnd;Lin:o9;gJ Lio:q1 cAbsNot);gJ Lip:gU cNEqual;q4 Lok;Liq:gU cLessOrEq;wE(cLessOrEq);q4 TailCall_cLessOrEq;Lja:gU cLess;wE(cLess);q4 TailCall_cLess;Ljb:gU dK;wE(cGreaterOrEq);q4 TailCall_cGreaterOrEq;Ljc:gU o1;wE(cGreater);q4 TailCall_cGreater;Ljd:gU w8;q4 Lnj;Lje:g6 wE(cNot);q4 TailCall_cNot;Ljf:q7=fp_not m6 Ljg:gT fp_or(x d6 Ljh:gT fp_polar(x d6 Lji:dL Lnk;Ljj:qK=d8 cExp2;g0 wE(cExp2);q4 TailCall_cExp2;Ljk:qG1 dG Lnn;Ljl:qK dJ q1 h3 gX Lnn;Ljm:q7=g1 gX Lmq;Ljn:qM w3 Lmp;Ljo:qM q5 w5=cCbrt;g0 wE(cCbrt);q4 TailCall_cCbrt;Ljp:qM q1 cCbrt);Lol:w5=cInv;g0 q4 Lmn;Ljq:qM q4 Lep;Lka:qM w3 Lol;Lkb:q7=x+x;dI q4 Lno;Lkc:gT oX gW Lkd:q1 cTanh gX Lol;Lke:q7=h4 gX Lco;Lkf:q7=h4);gJ Lkg:q7=fp_real m6 Lkh:mM cSinCos gX Lol;Lki:q1 cSin oT Lkj:q7=fp_sin m6 Lkk:q1 cSqr o7 g1-1)gX Lmo;Lkl:q1 cSinh oT Lkm:q7=fp_sinh m6 Lkn:g6 q4 Lnq;Lko:hV 4 gC A);Lom:w5=cHypot;g0 wE(cHypot);q4 TailCall_cHypot;Lkp:hV 5 gC A oJ B gX Lom;Lkq:gU cAbs;q4 Llp;Lla:q7=fp_sqrt m6 Llb:g6 q4 Loi;Llc:gT y-x;q4 Lba;Lld:o9;q4 Loi;Lle:q8 oV oJ hS o7 x q71 gX Lmh;Llf:mM A oJ cSub gX Lmh;Llg:q1 cTan oT Llh:q7=fp_tan m6 Lli:q1 cTanh oT Llj:q7=fp_tanh m6 Llk:q7=fp_trunc m6 Lll:qW cDup);gJ Llm:dF=cDup;gJ Lln:hV 3 gC cSinCos);Lon:qE GetParamSwappedBinaryOpcode(C));gJ Llo:hV 3 gC cSinhCosh gX Lon;gJ q4 TailCall_cAcos;q4 TailCall_cAcosh;q4 TailCall_cAnd;q4 TailCall_cArg;q4 TailCall_cAsin;q4 TailCall_cAsinh;q4 TailCall_cAtan;q4 TailCall_cAtan2;q4 TailCall_cAtanh;q4 TailCall_cCeil;q4 TailCall_cConj;q4 TailCall_cFloor;q4 TailCall_cImag;q4 TailCall_cInt;q4 TailCall_cLog;q4 TailCall_cLog10;q4 TailCall_cLog2;q4 TailCall_cMax;q4 TailCall_cMin;q4 TailCall_cMod;q4 TailCall_cOr;q4 TailCall_cPolar;q4 TailCall_cRDiv;q4 TailCall_cRad;q4 TailCall_cReal;q4 TailCall_cSec;q4 TailCall_cSin;q4 TailCall_cSinh;q4 TailCall_cSqrt;q4 TailCall_cSub;q4 TailCall_cTan;q4 TailCall_cTanh;q4 TailCall_cTrunc; #endif #undef FP_ReDefinePointers #undef FP_TRACE_BYTECODE_OPTIMIZATION #undef FP_TRACE_OPCODENAME fparserc++-4.5.2/extrasrc/fpaux.hh000066400000000000000000001276521332731714600170000ustar00rootroot00000000000000/***************************************************************************\ |* Function Parser for C++ v4.5.2 *| |*-------------------------------------------------------------------------*| |* Copyright: Juha Nieminen, Joel Yliluoma *| |* *| |* This library is distributed under the terms of the *| |* GNU Lesser General Public License version 3. *| |* (See lgpl.txt and gpl.txt for the license text.) *| \***************************************************************************/ // NOTE: // This file contains only internal types for the function parser library. // You don't need to include this file in your code. Include "fparser.hh" // only. #ifndef ONCE_FPARSER_AUX_H_ #define ONCE_FPARSER_AUX_H_ #include "fptypes.hh" #include #ifdef FP_SUPPORT_MPFR_FLOAT_TYPE #include "mpfr/MpfrFloat.hh" #endif #ifdef FP_SUPPORT_GMP_INT_TYPE #include "mpfr/GmpInt.hh" #endif #ifdef FP_SUPPORT_COMPLEX_NUMBERS #include #endif #ifdef ONCE_FPARSER_H_ namespace FUNCTIONPARSERTYPES { template struct IsIntType { enum { result = false }; }; template<> struct IsIntType { enum { result = true }; }; #ifdef FP_SUPPORT_GMP_INT_TYPE template<> struct IsIntType { enum { result = true }; }; #endif template struct IsComplexType { enum { result = false }; }; #ifdef FP_SUPPORT_COMPLEX_NUMBERS template struct IsComplexType > { enum { result = true }; }; #endif //========================================================================== // Constants //========================================================================== template inline Value_t fp_const_pi() // CONSTANT_PI { return Value_t(3.1415926535897932384626433832795028841971693993751L); } template inline Value_t fp_const_e() // CONSTANT_E { return Value_t(2.7182818284590452353602874713526624977572L); } template inline Value_t fp_const_einv() // CONSTANT_EI { return Value_t(0.367879441171442321595523770161460867445811131L); } template inline Value_t fp_const_log2() // CONSTANT_L2, CONSTANT_L2EI { return Value_t(0.69314718055994530941723212145817656807550013436025525412L); } template inline Value_t fp_const_log10() // CONSTANT_L10, CONSTANT_L10EI { return Value_t(2.302585092994045684017991454684364207601101488628772976L); } template inline Value_t fp_const_log2inv() // CONSTANT_L2I, CONSTANT_L2E { return Value_t(1.442695040888963407359924681001892137426645954L); } template inline Value_t fp_const_log10inv() // CONSTANT_L10I, CONSTANT_L10E { return Value_t(0.434294481903251827651128918916605082294397L); } template inline const Value_t& fp_const_deg_to_rad() // CONSTANT_DR { static const Value_t factor = fp_const_pi() / Value_t(180); // to rad from deg return factor; } template inline const Value_t& fp_const_rad_to_deg() // CONSTANT_RD { static const Value_t factor = Value_t(180) / fp_const_pi(); // to deg from rad return factor; } #ifdef FP_SUPPORT_MPFR_FLOAT_TYPE template<> inline MpfrFloat fp_const_pi() { return MpfrFloat::const_pi(); } template<> inline MpfrFloat fp_const_e() { return MpfrFloat::const_e(); } template<> inline MpfrFloat fp_const_einv() { return MpfrFloat(1) / MpfrFloat::const_e(); } template<> inline MpfrFloat fp_const_log2() { return MpfrFloat::const_log2(); } /* template<> inline MpfrFloat fp_const_log10() { return fp_log(MpfrFloat(10)); } template<> inline MpfrFloat fp_const_log2inv() { return MpfrFloat(1) / MpfrFloat::const_log2(); } template<> inline MpfrFloat fp_const_log10inv() { return fp_log10(MpfrFloat::const_e()); } */ #endif //========================================================================== // Generic math functions //========================================================================== template inline Value_t fp_abs(const Value_t& x) { return std::fabs(x); } template inline Value_t fp_acos(const Value_t& x) { return std::acos(x); } template inline Value_t fp_asin(const Value_t& x) { return std::asin(x); } template inline Value_t fp_atan(const Value_t& x) { return std::atan(x); } template inline Value_t fp_atan2(const Value_t& x, const Value_t& y) { return std::atan2(x, y); } template inline Value_t fp_ceil(const Value_t& x) { return std::ceil(x); } template inline Value_t fp_cos(const Value_t& x) { return std::cos(x); } template inline Value_t fp_cosh(const Value_t& x) { return std::cosh(x); } template inline Value_t fp_exp(const Value_t& x) { return std::exp(x); } template inline Value_t fp_floor(const Value_t& x) { return std::floor(x); } template inline Value_t fp_log(const Value_t& x) { return std::log(x); } template inline Value_t fp_mod(const Value_t& x, const Value_t& y) { return std::fmod(x, y); } template inline Value_t fp_sin(const Value_t& x) { return std::sin(x); } template inline Value_t fp_sinh(const Value_t& x) { return std::sinh(x); } template inline Value_t fp_sqrt(const Value_t& x) { return std::sqrt(x); } template inline Value_t fp_tan(const Value_t& x) { return std::tan(x); } template inline Value_t fp_tanh(const Value_t& x) { return std::tanh(x); } #ifdef FP_SUPPORT_CPLUSPLUS11_MATH_FUNCS template inline Value_t fp_asinh(const Value_t& x) { return std::asinh(x); } template inline Value_t fp_acosh(const Value_t& x) { return std::acosh(x); } template inline Value_t fp_atanh(const Value_t& x) { return std::atanh(x); } #else template inline Value_t fp_asinh(const Value_t& x) { return fp_log(x + fp_sqrt(x*x + Value_t(1))); } template inline Value_t fp_acosh(const Value_t& x) { return fp_log(x + fp_sqrt(x*x - Value_t(1))); } template inline Value_t fp_atanh(const Value_t& x) { return fp_log( (Value_t(1)+x) / (Value_t(1)-x)) * Value_t(0.5); // Note: x = +1 causes division by zero // x = -1 causes log(0) // Thus, x must not be +-1 } #endif // FP_SUPPORT_ASINH #ifdef FP_SUPPORT_CPLUSPLUS11_MATH_FUNCS template inline Value_t fp_hypot(const Value_t& x, const Value_t& y) { return std::hypot(x,y); } template inline std::complex fp_hypot (const std::complex& x, const std::complex& y) { return fp_sqrt(x*x + y*y); } #else template inline Value_t fp_hypot(const Value_t& x, const Value_t& y) { return fp_sqrt(x*x + y*y); } #endif template inline Value_t fp_pow_base(const Value_t& x, const Value_t& y) { return std::pow(x, y); } #ifdef FP_SUPPORT_CPLUSPLUS11_MATH_FUNCS template inline Value_t fp_log2(const Value_t& x) { return std::log2(x); } template inline std::complex fp_log2(const std::complex& x) { return fp_log(x) * fp_const_log2inv(); } #else template inline Value_t fp_log2(const Value_t& x) { return fp_log(x) * fp_const_log2inv(); } #endif // FP_SUPPORT_LOG2 template inline Value_t fp_log10(const Value_t& x) { return fp_log(x) * fp_const_log10inv(); } template inline Value_t fp_trunc(const Value_t& x) { return x < Value_t() ? fp_ceil(x) : fp_floor(x); } template inline Value_t fp_int(const Value_t& x) { return x < Value_t() ? fp_ceil(x - Value_t(0.5)) : fp_floor(x + Value_t(0.5)); } template inline void fp_sinCos(Value_t& sinvalue, Value_t& cosvalue, const Value_t& param) { // Assuming that "cosvalue" and "param" do not // overlap, but "sinvalue" and "param" may. cosvalue = fp_cos(param); sinvalue = fp_sin(param); } template inline void fp_sinhCosh(Value_t& sinhvalue, Value_t& coshvalue, const Value_t& param) { const Value_t ex(fp_exp(param)), emx(fp_exp(-param)); sinhvalue = Value_t(0.5)*(ex-emx); coshvalue = Value_t(0.5)*(ex+emx); } template struct Epsilon { static Value_t value; static Value_t defaultValue() { return 0; } }; template<> inline double Epsilon::defaultValue() { return 1E-12; } template<> inline float Epsilon::defaultValue() { return 1E-5F; } template<> inline long double Epsilon::defaultValue() { return 1E-14L; } template<> inline std::complex Epsilon >::defaultValue() { return 1E-12; } template<> inline std::complex Epsilon >::defaultValue() { return 1E-5F; } template<> inline std::complex Epsilon >::defaultValue() { return 1E-14L; } #ifdef FP_SUPPORT_MPFR_FLOAT_TYPE template<> inline MpfrFloat Epsilon::defaultValue() { return MpfrFloat::someEpsilon(); } #endif template Value_t Epsilon::value = Epsilon::defaultValue(); #ifdef _GNU_SOURCE inline void fp_sinCos(double& sin, double& cos, const double& a) { sincos(a, &sin, &cos); } inline void fp_sinCos(float& sin, float& cos, const float& a) { sincosf(a, &sin, &cos); } inline void fp_sinCos(long double& sin, long double& cos, const long double& a) { sincosl(a, &sin, &cos); } #endif // ------------------------------------------------------------------------- // Long int // ------------------------------------------------------------------------- inline long fp_abs(const long& x) { return x < 0 ? -x : x; } inline long fp_acos(const long&) { return 0; } inline long fp_asin(const long&) { return 0; } inline long fp_atan(const long&) { return 0; } inline long fp_atan2(const long&, const long&) { return 0; } inline long fp_cbrt(const long&) { return 0; } inline long fp_ceil(const long& x) { return x; } inline long fp_cos(const long&) { return 0; } inline long fp_cosh(const long&) { return 0; } inline long fp_exp(const long&) { return 0; } inline long fp_exp2(const long&) { return 0; } inline long fp_floor(const long& x) { return x; } inline long fp_log(const long&) { return 0; } inline long fp_log2(const long&) { return 0; } inline long fp_log10(const long&) { return 0; } inline long fp_mod(const long& x, const long& y) { return x % y; } inline long fp_pow(const long&, const long&) { return 0; } inline long fp_sin(const long&) { return 0; } inline long fp_sinh(const long&) { return 0; } inline long fp_sqrt(const long&) { return 1; } inline long fp_tan(const long&) { return 0; } inline long fp_tanh(const long&) { return 0; } inline long fp_asinh(const long&) { return 0; } inline long fp_acosh(const long&) { return 0; } inline long fp_atanh(const long&) { return 0; } inline long fp_pow_base(const long&, const long&) { return 0; } inline void fp_sinCos(long&, long&, const long&) {} inline void fp_sinhCosh(long&, long&, const long&) {} //template<> inline long fp_epsilon() { return 0; } // ------------------------------------------------------------------------- // MpfrFloat // ------------------------------------------------------------------------- #ifdef FP_SUPPORT_MPFR_FLOAT_TYPE inline MpfrFloat fp_abs(const MpfrFloat& x) { return MpfrFloat::abs(x); } inline MpfrFloat fp_acos(const MpfrFloat& x) { return MpfrFloat::acos(x); } inline MpfrFloat fp_acosh(const MpfrFloat& x) { return MpfrFloat::acosh(x); } inline MpfrFloat fp_asin(const MpfrFloat& x) { return MpfrFloat::asin(x); } inline MpfrFloat fp_asinh(const MpfrFloat& x) { return MpfrFloat::asinh(x); } inline MpfrFloat fp_atan(const MpfrFloat& x) { return MpfrFloat::atan(x); } inline MpfrFloat fp_atan2(const MpfrFloat& x, const MpfrFloat& y) { return MpfrFloat::atan2(x, y); } inline MpfrFloat fp_atanh(const MpfrFloat& x) { return MpfrFloat::atanh(x); } inline MpfrFloat fp_cbrt(const MpfrFloat& x) { return MpfrFloat::cbrt(x); } inline MpfrFloat fp_ceil(const MpfrFloat& x) { return MpfrFloat::ceil(x); } inline MpfrFloat fp_cos(const MpfrFloat& x) { return MpfrFloat::cos(x); } inline MpfrFloat fp_cosh(const MpfrFloat& x) { return MpfrFloat::cosh(x); } inline MpfrFloat fp_exp(const MpfrFloat& x) { return MpfrFloat::exp(x); } inline MpfrFloat fp_exp2(const MpfrFloat& x) { return MpfrFloat::exp2(x); } inline MpfrFloat fp_floor(const MpfrFloat& x) { return MpfrFloat::floor(x); } inline MpfrFloat fp_hypot(const MpfrFloat& x, const MpfrFloat& y) { return MpfrFloat::hypot(x, y); } inline MpfrFloat fp_int(const MpfrFloat& x) { return MpfrFloat::round(x); } inline MpfrFloat fp_log(const MpfrFloat& x) { return MpfrFloat::log(x); } inline MpfrFloat fp_log2(const MpfrFloat& x) { return MpfrFloat::log2(x); } inline MpfrFloat fp_log10(const MpfrFloat& x) { return MpfrFloat::log10(x); } inline MpfrFloat fp_mod(const MpfrFloat& x, const MpfrFloat& y) { return x % y; } inline MpfrFloat fp_sin(const MpfrFloat& x) { return MpfrFloat::sin(x); } inline MpfrFloat fp_sinh(const MpfrFloat& x) { return MpfrFloat::sinh(x); } inline MpfrFloat fp_sqrt(const MpfrFloat& x) { return MpfrFloat::sqrt(x); } inline MpfrFloat fp_tan(const MpfrFloat& x) { return MpfrFloat::tan(x); } inline MpfrFloat fp_tanh(const MpfrFloat& x) { return MpfrFloat::tanh(x); } inline MpfrFloat fp_trunc(const MpfrFloat& x) { return MpfrFloat::trunc(x); } inline MpfrFloat fp_pow(const MpfrFloat& x, const MpfrFloat& y) { return MpfrFloat::pow(x, y); } inline MpfrFloat fp_pow_base(const MpfrFloat& x, const MpfrFloat& y) { return MpfrFloat::pow(x, y); } inline void fp_sinCos(MpfrFloat& sin, MpfrFloat& cos, const MpfrFloat& a) { MpfrFloat::sincos(a, sin, cos); } inline void fp_sinhCosh(MpfrFloat& sinhvalue, MpfrFloat& coshvalue, const MpfrFloat& param) { const MpfrFloat paramCopy = param; sinhvalue = fp_sinh(paramCopy); coshvalue = fp_cosh(paramCopy); } #endif // FP_SUPPORT_MPFR_FLOAT_TYPE // ------------------------------------------------------------------------- // GMP int // ------------------------------------------------------------------------- #ifdef FP_SUPPORT_GMP_INT_TYPE inline GmpInt fp_abs(const GmpInt& x) { return GmpInt::abs(x); } inline GmpInt fp_acos(const GmpInt&) { return 0; } inline GmpInt fp_acosh(const GmpInt&) { return 0; } inline GmpInt fp_asin(const GmpInt&) { return 0; } inline GmpInt fp_asinh(const GmpInt&) { return 0; } inline GmpInt fp_atan(const GmpInt&) { return 0; } inline GmpInt fp_atan2(const GmpInt&, const GmpInt&) { return 0; } inline GmpInt fp_atanh(const GmpInt&) { return 0; } inline GmpInt fp_cbrt(const GmpInt&) { return 0; } inline GmpInt fp_ceil(const GmpInt& x) { return x; } inline GmpInt fp_cos(const GmpInt&) { return 0; } inline GmpInt fp_cosh(const GmpInt&) { return 0; } inline GmpInt fp_exp(const GmpInt&) { return 0; } inline GmpInt fp_exp2(const GmpInt&) { return 0; } inline GmpInt fp_floor(const GmpInt& x) { return x; } inline GmpInt fp_hypot(const GmpInt&, const GmpInt&) { return 0; } inline GmpInt fp_int(const GmpInt& x) { return x; } inline GmpInt fp_log(const GmpInt&) { return 0; } inline GmpInt fp_log2(const GmpInt&) { return 0; } inline GmpInt fp_log10(const GmpInt&) { return 0; } inline GmpInt fp_mod(const GmpInt& x, const GmpInt& y) { return x % y; } inline GmpInt fp_pow(const GmpInt&, const GmpInt&) { return 0; } inline GmpInt fp_sin(const GmpInt&) { return 0; } inline GmpInt fp_sinh(const GmpInt&) { return 0; } inline GmpInt fp_sqrt(const GmpInt&) { return 0; } inline GmpInt fp_tan(const GmpInt&) { return 0; } inline GmpInt fp_tanh(const GmpInt&) { return 0; } inline GmpInt fp_trunc(const GmpInt& x) { return x; } inline GmpInt fp_pow_base(const GmpInt&, const GmpInt&) { return 0; } inline void fp_sinCos(GmpInt&, GmpInt&, const GmpInt&) {} inline void fp_sinhCosh(GmpInt&, GmpInt&, const GmpInt&) {} #endif // FP_SUPPORT_GMP_INT_TYPE #ifdef FP_SUPPORT_CPLUSPLUS11_MATH_FUNCS template inline Value_t fp_cbrt(const Value_t& x) { return std::cbrt(x); } #else template inline Value_t fp_cbrt(const Value_t& x) { return (x > Value_t() ? fp_exp(fp_log( x) / Value_t(3)) : x < Value_t() ? -fp_exp(fp_log(-x) / Value_t(3)) : Value_t()); } #endif // ------------------------------------------------------------------------- // Synthetic functions and fallbacks for when an optimized // implementation or a library function is not available // ------------------------------------------------------------------------- template inline Value_t fp_arg(const Value_t& x); template inline Value_t fp_exp2(const Value_t& x); template inline Value_t fp_int(const Value_t& x); template inline Value_t fp_trunc(const Value_t& x); template inline void fp_sinCos(Value_t& , Value_t& , const Value_t& ); template inline void fp_sinhCosh(Value_t& , Value_t& , const Value_t& ); #ifdef FP_SUPPORT_COMPLEX_NUMBERS /* NOTE: Complex multiplication of a and b can be done with: tmp = b.real * (a.real + a.imag) result.real = tmp - a.imag * (b.real + b.imag) result.imag = tmp + a.real * (b.imag - b.real) This has fewer multiplications than the standard algorithm. Take note, if you support mpfr complex one day. */ template struct FP_ProbablyHasFastLibcComplex { enum { result = false }; }; /* The generic sqrt() etc. implementations in libstdc++ * are very plain and non-optimized; however, it contains * callbacks to libc complex math functions where possible, * and I suspect that those may actually be well optimized. * So we use std:: functions when we suspect they may be fast, * and otherwise we use our own optimized implementations. */ #ifdef __GNUC__ template<> struct FP_ProbablyHasFastLibcComplex { enum { result = true }; }; template<> struct FP_ProbablyHasFastLibcComplex { enum { result = true }; }; template<> struct FP_ProbablyHasFastLibcComplex { enum { result = true }; }; #endif template inline const std::complex fp_make_imag(const std::complex& v) { return std::complex ( T(), v.real() ); } template inline std::complex fp_real(const std::complex& x) { return x.real(); } template inline std::complex fp_imag(const std::complex& x) { return x.imag(); } template inline std::complex fp_arg(const std::complex& x) { return std::arg(x); } template inline std::complex fp_conj(const std::complex& x) { return std::conj(x); } template inline std::complex fp_polar(const T& x, const T& y) { T si, co; fp_sinCos(si, co, y); return std::complex (x*co, x*si); } template inline std::complex fp_polar(const std::complex& x, const std::complex& y) { // x * cos(y) + i * x * sin(y) -- arguments are supposed to be REAL numbers return fp_polar (x.real(), y.real()); //return std::polar(x.real(), y.real()); //return x * (fp_cos(y) + (std::complex(0,1) * fp_sin(y)); } // These provide fallbacks in case there's no library function template inline std::complex fp_floor(const std::complex& x) { return std::complex (fp_floor(x.real()), fp_floor(x.imag())); } template inline std::complex fp_trunc(const std::complex& x) { return std::complex (fp_trunc(x.real()), fp_trunc(x.imag())); } template inline std::complex fp_int(const std::complex& x) { return std::complex (fp_int(x.real()), fp_int(x.imag())); } template inline std::complex fp_ceil(const std::complex& x) { return std::complex (fp_ceil(x.real()), fp_ceil(x.imag())); } template inline std::complex fp_abs(const std::complex& x) { return std::abs(x); //T extent = fp_max(fp_abs(x.real()), fp_abs(x.imag())); //if(extent == T()) return x; //return extent * fp_hypot(x.real() / extent, x.imag() / extent); } template inline std::complex fp_exp(const std::complex& x) { if(FP_ProbablyHasFastLibcComplex::result) return std::exp(x); return fp_polar(fp_exp(x.real()), x.imag()); } template inline std::complex fp_log(const std::complex& x) { if(FP_ProbablyHasFastLibcComplex::result) return std::log(x); // log(abs(x)) + i*arg(x) // log(Xr^2+Xi^2)*0.5 + i*arg(x) if(x.imag()==T()) return std::complex( fp_log(fp_abs(x.real())), fp_arg(x.real()) ); // Note: Uses real-value fp_arg() here! return std::complex( fp_log(std::norm(x)) * T(0.5), fp_arg(x).real() ); } template inline std::complex fp_sqrt(const std::complex& x) { if(FP_ProbablyHasFastLibcComplex::result) return std::sqrt(x); return fp_polar (fp_sqrt(fp_abs(x).real()), T(0.5)*fp_arg(x).real()); } template inline std::complex fp_acos(const std::complex& x) { // -i * log(x + i * sqrt(1 - x^2)) const std::complex i (T(), T(1)); return -i * fp_log(x + i * fp_sqrt(T(1) - x*x)); // Note: Real version of acos() cannot handle |x| > 1, // because it would cause sqrt(negative value). } template inline std::complex fp_asin(const std::complex& x) { // -i * log(i*x + sqrt(1 - x^2)) const std::complex i (T(), T(1)); return -i * fp_log(i*x + fp_sqrt(T(1) - x*x)); // Note: Real version of asin() cannot handle |x| > 1, // because it would cause sqrt(negative value). } template inline std::complex fp_atan(const std::complex& x) { // 0.5i * (log(1-i*x) - log(1+i*x)) // -0.5i * log( (1+i*x) / (1-i*x) ) const std::complex i (T(), T(1)); return (T(-0.5)*i) * fp_log( (T(1)+i*x) / (T(1)-i*x) ); // Note: x = -1i causes division by zero // x = +1i causes log(0) // Thus, x must not be +-1i } template inline std::complex fp_cos(const std::complex& x) { return std::cos(x); // // (exp(i*x) + exp(-i*x)) / (2) // //const std::complex i (T(), T(1)); // //return (fp_exp(i*x) + fp_exp(-i*x)) * T(0.5); // // Also: cos(Xr)*cosh(Xi) - i*sin(Xr)*sinh(Xi) // return std::complex ( // fp_cos(x.real())*fp_cosh(x.imag()), // -fp_sin(x.real())*fp_sinh(x.imag())); } template inline std::complex fp_sin(const std::complex& x) { return std::sin(x); // // (exp(i*x) - exp(-i*x)) / (2i) // //const std::complex i (T(), T(1)); // //return (fp_exp(i*x) - fp_exp(-i*x)) * (T(-0.5)*i); // // Also: sin(Xr)*cosh(Xi) + cos(Xr)*sinh(Xi) // return std::complex ( // fp_sin(x.real())*fp_cosh(x.imag()), // fp_cos(x.real())*fp_sinh(x.imag())); } template inline void fp_sinCos( std::complex& sinvalue, std::complex& cosvalue, const std::complex& x) { //const std::complex i (T(), T(1)), expix(fp_exp(i*x)), expmix(fp_exp((-i)*x)); //cosvalue = (expix + expmix) * T(0.5); //sinvalue = (expix - expmix) * (i*T(-0.5)); // The above expands to the following: T srx, crx; fp_sinCos(srx, crx, x.real()); T six, cix; fp_sinhCosh(six, cix, x.imag()); sinvalue = std::complex(srx*cix, crx*six); cosvalue = std::complex(crx*cix, -srx*six); } template inline void fp_sinhCosh( std::complex& sinhvalue, std::complex& coshvalue, const std::complex& x) { T srx, crx; fp_sinhCosh(srx, crx, x.real()); T six, cix; fp_sinCos(six, cix, x.imag()); sinhvalue = std::complex(srx*cix, crx*six); coshvalue = std::complex(crx*cix, srx*six); } template inline std::complex fp_tan(const std::complex& x) { return std::tan(x); //std::complex si, co; //fp_sinCos(si, co, x); //return si/co; // // (i-i*exp(2i*x)) / (exp(2i*x)+1) // const std::complex i (T(), T(1)), exp2ix=fp_exp((2*i)*x); // return (i-i*exp2ix) / (exp2ix+T(1)); // // Also: sin(x)/cos(y) // // return fp_sin(x)/fp_cos(x); } template inline std::complex fp_cosh(const std::complex& x) { return std::cosh(x); // // (exp(x) + exp(-x)) * 0.5 // // Also: cosh(Xr)*cos(Xi) + i*sinh(Xr)*sin(Xi) // return std::complex ( // fp_cosh(x.real())*fp_cos(x.imag()), // fp_sinh(x.real())*fp_sin(x.imag())); } template inline std::complex fp_sinh(const std::complex& x) { return std::sinh(x); // // (exp(x) - exp(-x)) * 0.5 // // Also: sinh(Xr)*cos(Xi) + i*cosh(Xr)*sin(Xi) // return std::complex ( // fp_sinh(x.real())*fp_cos(x.imag()), // fp_cosh(x.real())*fp_sin(x.imag())); } template inline std::complex fp_tanh(const std::complex& x) { return std::tanh(x); //std::complex si, co; //fp_sinhCosh(si, co, x); //return si/co; // // (exp(2*x)-1) / (exp(2*x)+1) // // Also: sinh(x)/tanh(x) // const std::complex exp2x=fp_exp(x+x); // return (exp2x-T(1)) / (exp2x+T(1)); } #ifdef FP_SUPPORT_CPLUSPLUS11_MATH_FUNCS template inline std::complex fp_acosh(const std::complex& x) { return fp_log(x + fp_sqrt(x*x - std::complex(1))); } template inline std::complex fp_asinh(const std::complex& x) { return fp_log(x + fp_sqrt(x*x + std::complex(1))); } template inline std::complex fp_atanh(const std::complex& x) { return fp_log( (std::complex(1)+x) / (std::complex(1)-x)) * std::complex(0.5); } #endif template inline std::complex fp_pow(const std::complex& x, const std::complex& y) { // return std::pow(x,y); // With complex numbers, pow(x,y) can be solved with // the general formula: exp(y*log(x)). It handles // all special cases gracefully. // It expands to the following: // A) // t1 = log(x) // t2 = y * t1 // res = exp(t2) // B) // t1.r = log(x.r * x.r + x.i * x.i) * 0.5 \ fp_log() // t1.i = atan2(x.i, x.r) / // t2.r = y.r*t1.r - y.i*t1.i \ multiplication // t2.i = y.r*t1.i + y.i*t1.r / // rho = exp(t2.r) \ fp_exp() // theta = t2.i / // res.r = rho * cos(theta) \ fp_polar(), called from // res.i = rho * sin(theta) / fp_exp(). Uses sincos(). // Aside from the common "norm" calculation in atan2() // and in the log parameter, both of which are part of fp_log(), // there does not seem to be any incentive to break this // function down further; it would not help optimizing it. // However, we do handle the following special cases: // // When x is real (positive or negative): // t1.r = log(abs(x.r)) // t1.i = x.r<0 ? -pi : 0 // When y is real: // t2.r = y.r * t1.r // t2.i = y.r * t1.i const std::complex t = (x.imag() != T()) ? fp_log(x) : std::complex (fp_log(fp_abs(x.real())), fp_arg(x.real())); // Note: Uses real-value fp_arg() here! return y.imag() != T() ? fp_exp(y * t) : fp_polar (fp_exp(y.real()*t.real()), y.real()*t.imag()); } template inline std::complex fp_cbrt(const std::complex& x) { // For real numbers, prefer giving a real solution // rather than a complex solution. // For example, cbrt(-3) has the following three solutions: // A) 0.7211247966535 + 1.2490247864016i // B) 0.7211247966535 - 1.2490247864016i // C) -1.442249593307 // exp(log(x)/3) gives A, but we prefer to give C. if(x.imag() == T()) return fp_cbrt(x.real()); const std::complex t(fp_log(x)); return fp_polar (fp_exp(t.real() / T(3)), t.imag() / T(3)); } template inline std::complex fp_exp2(const std::complex& x) { // pow(2, x) // polar(2^Xr, Xi*log(2)) return fp_polar (fp_exp2(x.real()), x.imag()*fp_const_log2()); } template inline std::complex fp_mod(const std::complex& x, const std::complex& y) { // Modulo function is probably not defined for complex numbers. // But we do our best to calculate it the same way as it is done // with real numbers, so that at least it is in some way "consistent". if(y.imag() == 0) return fp_mod(x.real(), y.real()); // optimization std::complex n = fp_trunc(x / y); return x - n * y; } /* libstdc++ already defines a streaming operator for complex values, * but we redefine our own that it is compatible with the input * accepted by fparser. I.e. instead of (5,3) we now get (5+3i), * and instead of (-4,0) we now get -4. */ template inline std::ostream& operator<<(std::ostream& os, const std::complex& value) { if(value.imag() == T()) return os << value.real(); if(value.real() == T()) return os << value.imag() << 'i'; if(value.imag() < T()) return os << '(' << value.real() << "-" << -value.imag() << "i)"; else return os << '(' << value.real() << "+" << value.imag() << "i)"; } /* Less-than or greater-than operators are not technically defined * for Complex types. However, in fparser and its tool set, these * operators are widely required to be present. * Our implementation here is based on converting the complex number * into a scalar and the doing a scalar comparison on the value. * The means by which the number is changed into a scalar is based * on the following principles: * - Does not introduce unjustified amounts of extra inaccuracy * - Is transparent to purely real values * (this disqualifies something like x.real() + x.imag()) * - Does not ignore the imaginary value completely * (this may be relevant especially in testbed) * - Is not so complicated that it would slow down a great deal * * Basically our formula here is the same as std::abs(), * except that it keeps the sign of the original real number, * and it does not do a sqrt() calculation that is not really * needed because we are only interested in the relative magnitudes. * * Equality and nonequality operators must not need to be overloaded. * They are already implemented in standard, and we must * not introduce flawed equality assumptions. */ template inline T fp_complexScalarize(const std::complex& x) { T res(std::norm(x)); if(x.real() < T()) res = -res; return res; } template inline T fp_realComplexScalarize(const T& x) { T res(x*x); if(x < T()) res = -res; return res; } // { return x.real() * (T(1.0) + fp_abs(x.imag())); } #define d(op) \ template \ inline bool operator op (const std::complex& x, T y) \ { return fp_complexScalarize(x) op fp_realComplexScalarize(y); } \ template \ inline bool operator op (const std::complex& x, const std::complex& y) \ { return fp_complexScalarize(x) op \ fp_complexScalarize(y); } \ template \ inline bool operator op (T x, const std::complex& y) \ { return fp_realComplexScalarize(x) op fp_complexScalarize(y); } d( < ) d( <= ) d( > ) d( >= ) #undef d #endif template inline Value_t fp_real(const Value_t& x) { return x; } template inline Value_t fp_imag(const Value_t& ) { return Value_t(); } template inline Value_t fp_arg(const Value_t& x) { return x < Value_t() ? -fp_const_pi() : Value_t(); } template inline Value_t fp_conj(const Value_t& x) { return x; } template inline Value_t fp_polar(const Value_t& x, const Value_t& y) { return x * fp_cos(y); } // This is of course a meaningless function. template inline std::complex fp_atan2(const std::complex& y, const std::complex& x) { if(y == Value_t()) return fp_arg(x); if(x == Value_t()) return fp_const_pi() * Value_t(-0.5); // 2*atan(y / (sqrt(x^2+y^2) + x) ) // 2*atan( (sqrt(x^2+y^2) - x) / y) std::complex res( fp_atan(y / (fp_hypot(x,y) + x)) ); return res+res; } // ------------------------------------------------------------------------- // Comparison // ------------------------------------------------------------------------- template inline bool fp_equal(const Value_t& x, const Value_t& y) { return IsIntType::result ? (x == y) : (fp_abs(x - y) <= Epsilon::value); } template inline bool fp_nequal(const Value_t& x, const Value_t& y) { return IsIntType::result ? (x != y) : (fp_abs(x - y) > Epsilon::value); } template inline bool fp_less(const Value_t& x, const Value_t& y) { return IsIntType::result ? (x < y) : (x < y - Epsilon::value); } template inline bool fp_lessOrEq(const Value_t& x, const Value_t& y) { return IsIntType::result ? (x <= y) : (x <= y + Epsilon::value); } template inline bool fp_greater(const Value_t& x, const Value_t& y) { return fp_less(y, x); } template inline bool fp_greaterOrEq(const Value_t& x, const Value_t& y) { return fp_lessOrEq(y, x); } template inline bool fp_truth(const Value_t& d) { return IsIntType::result ? d != Value_t() : fp_abs(d) >= Value_t(0.5); } template inline bool fp_absTruth(const Value_t& abs_d) { return IsIntType::result ? abs_d > Value_t() : abs_d >= Value_t(0.5); } template inline const Value_t& fp_min(const Value_t& d1, const Value_t& d2) { return d1 inline const Value_t& fp_max(const Value_t& d1, const Value_t& d2) { return d1>d2 ? d1 : d2; } template inline const Value_t fp_not(const Value_t& b) { return Value_t(!fp_truth(b)); } template inline const Value_t fp_notNot(const Value_t& b) { return Value_t(fp_truth(b)); } template inline const Value_t fp_absNot(const Value_t& b) { return Value_t(!fp_absTruth(b)); } template inline const Value_t fp_absNotNot(const Value_t& b) { return Value_t(fp_absTruth(b)); } template inline const Value_t fp_and(const Value_t& a, const Value_t& b) { return Value_t(fp_truth(a) && fp_truth(b)); } template inline const Value_t fp_or(const Value_t& a, const Value_t& b) { return Value_t(fp_truth(a) || fp_truth(b)); } template inline const Value_t fp_absAnd(const Value_t& a, const Value_t& b) { return Value_t(fp_absTruth(a) && fp_absTruth(b)); } template inline const Value_t fp_absOr(const Value_t& a, const Value_t& b) { return Value_t(fp_absTruth(a) || fp_absTruth(b)); } template inline const Value_t fp_make_imag(const Value_t& ) // Imaginary 1. In real mode, always zero. { return Value_t(); } ///////////// /* Opcode analysis functions are used by fp_opcode_add.inc */ /* Moved here from fparser.cc because fp_opcode_add.inc * is also now included by fpoptimizer.cc */ bool IsLogicalOpcode(unsigned op); bool IsComparisonOpcode(unsigned op); unsigned OppositeComparisonOpcode(unsigned op); bool IsNeverNegativeValueOpcode(unsigned op); bool IsAlwaysIntegerOpcode(unsigned op); bool IsUnaryOpcode(unsigned op); bool IsBinaryOpcode(unsigned op); bool IsVarOpcode(unsigned op); bool IsCommutativeOrParamSwappableBinaryOpcode(unsigned op); unsigned GetParamSwappedBinaryOpcode(unsigned op); template bool HasInvalidRangesOpcode(unsigned op); template inline Value_t DegreesToRadians(const Value_t& degrees) { return degrees * fp_const_deg_to_rad(); } template inline Value_t RadiansToDegrees(const Value_t& radians) { return radians * fp_const_rad_to_deg(); } template inline long makeLongInteger(const Value_t& value) { return (long) fp_int(value); } #ifdef FP_SUPPORT_COMPLEX_NUMBERS template inline long makeLongInteger(const std::complex& value) { return (long) fp_int( std::abs(value) ); } #endif // Is value an integer that fits in "long" datatype? template inline bool isLongInteger(const Value_t& value) { return value == Value_t( makeLongInteger(value) ); } template inline bool isOddInteger(const Value_t& value) { const Value_t halfValue = (value + Value_t(1)) * Value_t(0.5); return fp_equal(halfValue, fp_floor(halfValue)); } template inline bool isEvenInteger(const Value_t& value) { const Value_t halfValue = value * Value_t(0.5); return fp_equal(halfValue, fp_floor(halfValue)); } template inline bool isInteger(const Value_t& value) { return fp_equal(value, fp_floor(value)); } #ifdef FP_SUPPORT_LONG_INT_TYPE template<> inline bool isEvenInteger(const long& value) { return value%2 == 0; } template<> inline bool isInteger(const long&) { return true; } template<> inline bool isLongInteger(const long&) { return true; } template<> inline long makeLongInteger(const long& value) { return value; } #endif #ifdef FP_SUPPORT_MPFR_FLOAT_TYPE template<> inline bool isInteger(const MpfrFloat& value) { return value.isInteger(); } template<> inline bool isEvenInteger(const MpfrFloat& value) { return isInteger(value) && value%2 == 0; } template<> inline long makeLongInteger(const MpfrFloat& value) { return (long) value.toInt(); } #endif #ifdef FP_SUPPORT_GMP_INT_TYPE template<> inline bool isEvenInteger(const GmpInt& value) { return value%2 == 0; } template<> inline bool isInteger(const GmpInt&) { return true; } template<> inline long makeLongInteger(const GmpInt& value) { return (long) value.toInt(); } #endif #ifdef FP_SUPPORT_LONG_INT_TYPE template<> inline bool isOddInteger(const long& value) { return value%2 != 0; } #endif #ifdef FP_SUPPORT_MPFR_FLOAT_TYPE template<> inline bool isOddInteger(const MpfrFloat& value) { return value.isInteger() && value%2 != 0; } #endif #ifdef FP_SUPPORT_GMP_INT_TYPE template<> inline bool isOddInteger(const GmpInt& value) { return value%2 != 0; } #endif // ------------------------------------------------------------------------- // fp_pow // ------------------------------------------------------------------------- // Commented versions in fparser.cc template inline Value_t fp_pow_with_exp_log(const Value_t& x, const Value_t& y) { return fp_exp(fp_log(x) * y); } template inline Value_t fp_powi(Value_t x, unsigned long y) { Value_t result(1); while(y != 0) { if(y & 1) { result *= x; y -= 1; } else { x *= x; y /= 2; } } return result; } template Value_t fp_pow(const Value_t& x, const Value_t& y) { if(x == Value_t(1)) return Value_t(1); if(isLongInteger(y)) { if(y >= Value_t(0)) return fp_powi(x, makeLongInteger(y)); else return Value_t(1) / fp_powi(x, -makeLongInteger(y)); } if(y >= Value_t(0)) { if(x > Value_t(0)) return fp_pow_with_exp_log(x, y); if(x == Value_t(0)) return Value_t(0); if(!isInteger(y*Value_t(16))) return -fp_pow_with_exp_log(-x, y); } else { if(x > Value_t(0)) return fp_pow_with_exp_log(Value_t(1) / x, -y); if(x < Value_t(0)) { if(!isInteger(y*Value_t(-16))) return -fp_pow_with_exp_log(Value_t(-1) / x, -y); } } return fp_pow_base(x, y); } template inline Value_t fp_exp2(const Value_t& x) { return fp_pow(Value_t(2), x); } } // namespace FUNCTIONPARSERTYPES #endif // ONCE_FPARSER_H_ #endif // ONCE_FPARSER_AUX_H_ fparserc++-4.5.2/extrasrc/fptypes.hh000066400000000000000000000242331332731714600173360ustar00rootroot00000000000000/***************************************************************************\ |* Function Parser for C++ v4.5.2 *| |*-------------------------------------------------------------------------*| |* Copyright: Juha Nieminen, Joel Yliluoma *| |* *| |* This library is distributed under the terms of the *| |* GNU Lesser General Public License version 3. *| |* (See lgpl.txt and gpl.txt for the license text.) *| \***************************************************************************/ // NOTE: // This file contains only internal types for the function parser library. // You don't need to include this file in your code. Include "fparser.hh" // only. #ifndef ONCE_FPARSER_TYPES_H_ #define ONCE_FPARSER_TYPES_H_ #include "../fpconfig.hh" #include #ifdef ONCE_FPARSER_H_ #include #endif namespace FUNCTIONPARSERTYPES { enum OPCODE { // The order of opcodes in the function list must // match that which is in the Functions[] array. cAbs, cAcos, cAcosh, cArg, /* get the phase angle of a complex value */ cAsin, cAsinh, cAtan, cAtan2, cAtanh, cCbrt, cCeil, cConj, /* get the complex conjugate of a complex value */ cCos, cCosh, cCot, cCsc, cExp, cExp2, cFloor, cHypot, cIf, cImag, /* get imaginary part of a complex value */ cInt, cLog, cLog10, cLog2, cMax, cMin, cPolar, /* create a complex number from polar coordinates */ cPow, cReal, /* get real part of a complex value */ cSec, cSin, cSinh, cSqrt, cTan, cTanh, cTrunc, // These do not need any ordering: // Except that if you change the order of {eq,neq,lt,le,gt,ge}, you // must also change the order in ConstantFolding_ComparisonOperations(). cImmed, cJump, cNeg, cAdd, cSub, cMul, cDiv, cMod, cEqual, cNEqual, cLess, cLessOrEq, cGreater, cGreaterOrEq, cNot, cAnd, cOr, cNotNot, /* Protects the double-not sequence from optimizations */ cDeg, cRad, /* Multiplication and division by 180 / pi */ cFCall, cPCall, #ifdef FP_SUPPORT_OPTIMIZER cPopNMov, /* cPopNMov(x,y) moves [y] to [x] and deletes anything * above [x]. Used for disposing of temporaries. */ cLog2by, /* log2by(x,y) = log2(x) * y */ cNop, /* Used by fpoptimizer internally; should not occur in bytecode */ #endif cSinCos, /* sin(x) followed by cos(x) (two values are pushed to stack) */ cSinhCosh, /* hyperbolic equivalent of sincos */ cAbsAnd, /* As cAnd, but assume both operands are absolute values */ cAbsOr, /* As cOr, but assume both operands are absolute values */ cAbsNot, /* As cAbsNot, but assume the operand is an absolute value */ cAbsNotNot, /* As cAbsNotNot, but assume the operand is an absolute value */ cAbsIf, /* As cAbsIf, but assume the 1st operand is an absolute value */ cDup, /* Duplicates the last value in the stack: Push [Stacktop] */ cFetch, /* Same as Dup, except with absolute index * (next value is index) */ cInv, /* Inverts the last value in the stack (x = 1/x) */ cSqr, /* squares the last operand in the stack, no push/pop */ cRDiv, /* reverse division (not x/y, but y/x) */ cRSub, /* reverse subtraction (not x-y, but y-x) */ cRSqrt, /* inverse square-root (1/sqrt(x)) */ VarBegin }; #ifdef ONCE_FPARSER_H_ struct FuncDefinition { enum FunctionFlags { Enabled = 0x01, AngleIn = 0x02, AngleOut = 0x04, OkForInt = 0x08, ComplexOnly = 0x10 }; #ifdef FUNCTIONPARSER_SUPPORT_DEBUGGING const char name[8]; #endif unsigned params : 8; unsigned flags : 8; inline bool okForInt() const { return (flags & OkForInt) != 0; } inline bool complexOnly() const { return (flags & ComplexOnly) != 0; } }; #ifdef FUNCTIONPARSER_SUPPORT_DEBUGGING # define FP_FNAME(n) n, #else # define FP_FNAME(n) #endif // This list must be in the same order as that in OPCODE enum, // because the opcode value is used to index this array, and // the pointer to array element is used for generating the opcode. const FuncDefinition Functions[]= { /*cAbs */ { FP_FNAME("abs") 1, FuncDefinition::OkForInt }, /*cAcos */ { FP_FNAME("acos") 1, FuncDefinition::AngleOut }, /*cAcosh*/ { FP_FNAME("acosh") 1, FuncDefinition::AngleOut }, /*cArg */ { FP_FNAME("arg") 1, FuncDefinition::AngleOut | FuncDefinition::ComplexOnly }, /*cAsin */ { FP_FNAME("asin") 1, FuncDefinition::AngleOut }, /*cAsinh*/ { FP_FNAME("asinh") 1, FuncDefinition::AngleOut }, /*cAtan */ { FP_FNAME("atan") 1, FuncDefinition::AngleOut }, /*cAtan2*/ { FP_FNAME("atan2") 2, FuncDefinition::AngleOut }, /*cAtanh*/ { FP_FNAME("atanh") 1, 0 }, /*cCbrt */ { FP_FNAME("cbrt") 1, 0 }, /*cCeil */ { FP_FNAME("ceil") 1, 0 }, /*cConj */ { FP_FNAME("conj") 1, FuncDefinition::ComplexOnly }, /*cCos */ { FP_FNAME("cos") 1, FuncDefinition::AngleIn }, /*cCosh */ { FP_FNAME("cosh") 1, FuncDefinition::AngleIn }, /*cCot */ { FP_FNAME("cot") 1, FuncDefinition::AngleIn }, /*cCsc */ { FP_FNAME("csc") 1, FuncDefinition::AngleIn }, /*cExp */ { FP_FNAME("exp") 1, 0 }, /*cExp2 */ { FP_FNAME("exp2") 1, 0 }, /*cFloor*/ { FP_FNAME("floor") 1, 0 }, /*cHypot*/ { FP_FNAME("hypot") 2, 0 }, /*cIf */ { FP_FNAME("if") 0, FuncDefinition::OkForInt }, /*cImag */ { FP_FNAME("imag") 1, FuncDefinition::ComplexOnly }, /*cInt */ { FP_FNAME("int") 1, 0 }, /*cLog */ { FP_FNAME("log") 1, 0 }, /*cLog10*/ { FP_FNAME("log10") 1, 0 }, /*cLog2 */ { FP_FNAME("log2") 1, 0 }, /*cMax */ { FP_FNAME("max") 2, FuncDefinition::OkForInt }, /*cMin */ { FP_FNAME("min") 2, FuncDefinition::OkForInt }, /*cPolar */{ FP_FNAME("polar") 2, FuncDefinition::ComplexOnly | FuncDefinition::AngleIn }, /*cPow */ { FP_FNAME("pow") 2, 0 }, /*cReal */ { FP_FNAME("real") 1, FuncDefinition::ComplexOnly }, /*cSec */ { FP_FNAME("sec") 1, FuncDefinition::AngleIn }, /*cSin */ { FP_FNAME("sin") 1, FuncDefinition::AngleIn }, /*cSinh */ { FP_FNAME("sinh") 1, FuncDefinition::AngleIn }, /*cSqrt */ { FP_FNAME("sqrt") 1, 0 }, /*cTan */ { FP_FNAME("tan") 1, FuncDefinition::AngleIn }, /*cTanh */ { FP_FNAME("tanh") 1, FuncDefinition::AngleIn }, /*cTrunc*/ { FP_FNAME("trunc") 1, 0 } }; #undef FP_FNAME struct NamePtr { const char* name; unsigned nameLength; NamePtr(const char* n, unsigned l): name(n), nameLength(l) {} inline bool operator==(const NamePtr& rhs) const { return nameLength == rhs.nameLength && std::memcmp(name, rhs.name, nameLength) == 0; } inline bool operator<(const NamePtr& rhs) const { for(unsigned i = 0; i < nameLength; ++i) { if(i == rhs.nameLength) return false; const char c1 = name[i], c2 = rhs.name[i]; if(c1 < c2) return true; if(c2 < c1) return false; } return nameLength < rhs.nameLength; } }; template struct NameData { enum DataType { CONSTANT, UNIT, FUNC_PTR, PARSER_PTR, VARIABLE }; DataType type; unsigned index; Value_t value; NameData(DataType t, unsigned v) : type(t), index(v), value() { } NameData(DataType t, Value_t v) : type(t), index(), value(v) { } NameData() { } }; template class NamePtrsMap: public std::map > { }; const unsigned FUNC_AMOUNT = sizeof(Functions)/sizeof(Functions[0]); #endif // ONCE_FPARSER_H_ } #ifdef ONCE_FPARSER_H_ #include template struct FunctionParserBase::Data { unsigned mReferenceCounter; char mDelimiterChar; ParseErrorType mParseErrorType; int mEvalErrorType; bool mUseDegreeConversion; bool mHasByteCodeFlags; const char* mErrorLocation; unsigned mVariablesAmount; std::string mVariablesString; FUNCTIONPARSERTYPES::NamePtrsMap mNamePtrs; struct InlineVariable { FUNCTIONPARSERTYPES::NamePtr mName; unsigned mFetchIndex; }; typedef std::vector InlineVarNamesContainer; InlineVarNamesContainer mInlineVarNames; struct FuncWrapperPtrData { /* Only one of the pointers will point to a function, the other will be null. (The raw function pointer could be implemented as a FunctionWrapper specialization, but it's done like this for efficiency.) */ FunctionPtr mRawFuncPtr; FunctionWrapper* mFuncWrapperPtr; unsigned mParams; FuncWrapperPtrData(); ~FuncWrapperPtrData(); FuncWrapperPtrData(const FuncWrapperPtrData&); FuncWrapperPtrData& operator=(const FuncWrapperPtrData&); }; struct FuncParserPtrData { FunctionParserBase* mParserPtr; unsigned mParams; }; std::vector mFuncPtrs; std::vector mFuncParsers; std::vector mByteCode; std::vector mImmed; #if !defined(FP_USE_THREAD_SAFE_EVAL) && \ !defined(FP_USE_THREAD_SAFE_EVAL_WITH_ALLOCA) std::vector mStack; // Note: When mStack exists, // mStack.size() and mStackSize are mutually redundant. #endif unsigned mStackSize; Data(); Data(const Data&); Data& operator=(const Data&); // not implemented on purpose ~Data(); }; #endif //#include "fpaux.hh" #endif fparserc++-4.5.2/fparser.cc000066400000000000000000003660561332731714600154550ustar00rootroot00000000000000/***************************************************************************\ |* Function Parser for C++ v4.5.2 *| |*-------------------------------------------------------------------------*| |* Copyright: Juha Nieminen, Joel Yliluoma *| |* *| |* This library is distributed under the terms of the *| |* GNU Lesser General Public License version 3. *| |* (See lgpl.txt and gpl.txt for the license text.) *| \***************************************************************************/ #include "fpconfig.hh" #include "fparser.hh" #include #include #include #include #include #include #include #include "extrasrc/fptypes.hh" #include "extrasrc/fpaux.hh" using namespace FUNCTIONPARSERTYPES; #ifdef FP_USE_THREAD_SAFE_EVAL_WITH_ALLOCA #ifndef FP_USE_THREAD_SAFE_EVAL #define FP_USE_THREAD_SAFE_EVAL #endif #endif #ifdef __GNUC__ # define likely(x) __builtin_expect(!!(x), 1) # define unlikely(x) __builtin_expect(!!(x), 0) #else # define likely(x) (x) # define unlikely(x) (x) #endif //========================================================================= // Opcode analysis functions //========================================================================= // These functions are used by the Parse() bytecode optimizer (mostly from // code in fp_opcode_add.inc). bool FUNCTIONPARSERTYPES::IsLogicalOpcode(unsigned op) { switch(op) { case cAnd: case cAbsAnd: case cOr: case cAbsOr: case cNot: case cAbsNot: case cNotNot: case cAbsNotNot: case cEqual: case cNEqual: case cLess: case cLessOrEq: case cGreater: case cGreaterOrEq: return true; default: break; } return false; } bool FUNCTIONPARSERTYPES::IsComparisonOpcode(unsigned op) { switch(op) { case cEqual: case cNEqual: case cLess: case cLessOrEq: case cGreater: case cGreaterOrEq: return true; default: break; } return false; } unsigned FUNCTIONPARSERTYPES::OppositeComparisonOpcode(unsigned op) { switch(op) { case cLess: return cGreater; case cGreater: return cLess; case cLessOrEq: return cGreaterOrEq; case cGreaterOrEq: return cLessOrEq; } return op; } bool FUNCTIONPARSERTYPES::IsNeverNegativeValueOpcode(unsigned op) { switch(op) { case cAnd: case cAbsAnd: case cOr: case cAbsOr: case cNot: case cAbsNot: case cNotNot: case cAbsNotNot: case cEqual: case cNEqual: case cLess: case cLessOrEq: case cGreater: case cGreaterOrEq: case cSqrt: case cRSqrt: case cSqr: case cHypot: case cAbs: case cAcos: case cCosh: return true; default: break; } return false; } bool FUNCTIONPARSERTYPES::IsAlwaysIntegerOpcode(unsigned op) { switch(op) { case cAnd: case cAbsAnd: case cOr: case cAbsOr: case cNot: case cAbsNot: case cNotNot: case cAbsNotNot: case cEqual: case cNEqual: case cLess: case cLessOrEq: case cGreater: case cGreaterOrEq: case cInt: case cFloor: case cCeil: case cTrunc: return true; default: break; } return false; } bool FUNCTIONPARSERTYPES::IsUnaryOpcode(unsigned op) { switch(op) { case cInv: case cNeg: case cNot: case cAbsNot: case cNotNot: case cAbsNotNot: case cSqr: case cRSqrt: case cDeg: case cRad: return true; } return (op < FUNC_AMOUNT && Functions[op].params == 1); } bool FUNCTIONPARSERTYPES::IsBinaryOpcode(unsigned op) { switch(op) { case cAdd: case cSub: case cRSub: case cMul: case cDiv: case cRDiv: case cMod: case cEqual: case cNEqual: case cLess: case cLessOrEq: case cGreater: case cGreaterOrEq: case cAnd: case cAbsAnd: case cOr: case cAbsOr: return true; } return (op < FUNC_AMOUNT && Functions[op].params == 2); } bool FUNCTIONPARSERTYPES::IsVarOpcode(unsigned op) { // See comment in declaration of FP_ParamGuardMask return int(op) >= VarBegin; } bool FUNCTIONPARSERTYPES::IsCommutativeOrParamSwappableBinaryOpcode(unsigned op) { switch(op) { case cAdd: case cMul: case cEqual: case cNEqual: case cAnd: case cAbsAnd: case cOr: case cAbsOr: case cMin: case cMax: case cHypot: return true; case cDiv: case cSub: case cRDiv: case cRSub: return true; case cLess: case cGreater: case cLessOrEq: case cGreaterOrEq: return true; } return false; } unsigned FUNCTIONPARSERTYPES::GetParamSwappedBinaryOpcode(unsigned op) { switch(op) { case cAdd: case cMul: case cEqual: case cNEqual: case cAnd: case cAbsAnd: case cOr: case cAbsOr: case cMin: case cMax: case cHypot: return op; case cDiv: return cRDiv; case cSub: return cRSub; case cRDiv: return cDiv; case cRSub: return cSub; case cLess: return cGreater; case cGreater: return cLess; case cLessOrEq: return cGreaterOrEq; case cGreaterOrEq: return cLessOrEq; } return op; // Error } template bool FUNCTIONPARSERTYPES::HasInvalidRangesOpcode(unsigned op) { // Returns true, if the given opcode has a range of // input values that gives an error. if(ComplexType) { // COMPLEX: switch(op) { case cAtan: // allowed range: x != +-1i case cAtanh: // allowed range: x != +-1 //case cCot: // allowed range: tan(x) != 0 //case cCsc: // allowed range: sin(x) != 0 case cLog: // allowed range: x != 0 case cLog2: // allowed range: x != 0 case cLog10: // allowed range: x != 0 #ifdef FP_SUPPORT_OPTIMIZER case cLog2by:// allowed range: x != 0 #endif //case cPow: // allowed when: x != 0 or y != 0 //case cSec: // allowed range: cos(x) != 0 //case cTan: // allowed range: cos(x) != 0 --> x != +-(pi/2) //case cTanh: // allowed range: log(x) != -1 --> x != +-(pi/2)i case cRSqrt: // allowed range: x != 0 //case cDiv: // allowed range: y != 0 //case cRDiv: // allowed range: x != 0 //case cInv: // allowed range: x != 0 return true; } } else { // REAL: switch(op) { case cAcos: // allowed range: |x| <= 1 case cAsin: // allowed range: |x| <= 1 case cAcosh: // allowed range: x >= 1 case cAtanh: // allowed range: |x| < 1 //case cCot: // allowed range: tan(x) != 0 //case cCsc: // allowed range: sin(x) != 0 case cLog: // allowed range: x > 0 case cLog2: // allowed range: x > 0 case cLog10: // allowed range: x > 0 #ifdef FP_SUPPORT_OPTIMIZER case cLog2by:// allowed range: x > 0 #endif //case cPow: // allowed when: x > 0 or (x = 0 and y != 0) or (x<0) // Technically, when (x<0 and y is not integer), // it is not allowed, but we allow it anyway // in order to make nontrivial roots work. //case cSec: // allowed range: cos(x) != 0 case cSqrt: // allowed range: x >= 0 case cRSqrt: // allowed range: x > 0 //case cTan: // allowed range: cos(x) != 0 --> x != +-(pi/2) //case cDiv: // allowed range: y != 0 //case cRDiv: // allowed range: x != 0 //case cInv: // allowed range: x != 0 return true; } } return false; } template bool FUNCTIONPARSERTYPES::HasInvalidRangesOpcode(unsigned op); template bool FUNCTIONPARSERTYPES::HasInvalidRangesOpcode(unsigned op); #if(0) // Implementation moved to fpaux.hh due to linker problems //========================================================================= // Mathematical template functions //========================================================================= /* fp_pow() is a wrapper for std::pow() * that produces an identical value for * exp(1) ^ 2.0 (0x4000000000000000) * as exp(2.0) (0x4000000000000000) * - std::pow() on x86_64 * produces 2.0 (0x3FFFFFFFFFFFFFFF) instead! * See comments below for other special traits. */ namespace { template inline ValueT fp_pow_with_exp_log(const ValueT& x, const ValueT& y) { // Exponentiation using exp(log(x)*y). // See http://en.wikipedia.org/wiki/Exponentiation#Real_powers // Requirements: x > 0. return fp_exp(fp_log(x) * y); } template inline ValueT fp_powi(ValueT x, unsigned long y) { // Fast binary exponentiation algorithm // See http://en.wikipedia.org/wiki/Exponentiation_by_squaring // Requirements: y is non-negative integer. ValueT result(1); while(y != 0) { if(y & 1) { result *= x; y -= 1; } else { x *= x; y /= 2; } } return result; } } template ValueT FUNCTIONPARSERTYPES::fp_pow(const ValueT& x, const ValueT& y) { if(x == ValueT(1)) return ValueT(1); // y is now zero or positive if(isLongInteger(y)) { // Use fast binary exponentiation algorithm if(y >= ValueT(0)) return fp_powi(x, makeLongInteger(y)); else return ValueT(1) / fp_powi(x, -makeLongInteger(y)); } if(y >= ValueT(0)) { // y is now positive. Calculate using exp(log(x)*y). if(x > ValueT(0)) return fp_pow_with_exp_log(x, y); if(x == ValueT(0)) return ValueT(0); // At this point, y > 0.0 and x is known to be < 0.0, // because positive and zero cases are already handled. if(!isInteger(y*ValueT(16))) return -fp_pow_with_exp_log(-x, y); // ^This is not technically correct, but it allows // functions such as cbrt(x^5), that is, x^(5/3), // to be evaluated when x is negative. // It is too complicated (and slow) to test whether y // is a formed from a ratio of an integer to an odd integer. // (And due to floating point inaccuracy, pointless too.) // For example, x^1.30769230769... is // actually x^(17/13), i.e. (x^17) ^ (1/13). // (-5)^(17/13) gives us now -8.204227562330453. // To see whether the result is right, we can test the given // root: (-8.204227562330453)^13 gives us the value of (-5)^17, // which proves that the expression was correct. // // The y*16 check prevents e.g. (-4)^(3/2) from being calculated, // as it would confuse functioninfo when pow() returns no error // but sqrt() does when the formula is converted into sqrt(x)*x. // // The errors in this approach are: // (-2)^sqrt(2) should produce NaN // or actually sqrt(2)I + 2^sqrt(2), // produces -(2^sqrt(2)) instead. // (Impact: Neglible) // Thus, at worst, we're changing a NaN (or complex) // result into a negative real number result. } else { // y is negative. Utilize the x^y = 1/(x^-y) identity. if(x > ValueT(0)) return fp_pow_with_exp_log(ValueT(1) / x, -y); if(x < ValueT(0)) { if(!isInteger(y*ValueT(-16))) return -fp_pow_with_exp_log(ValueT(-1) / x, -y); // ^ See comment above. } // Remaining case: 0.0 ^ negative number } // This is reached when: // x=0, and y<0 // x<0, and y*16 is either positive or negative integer // It is used for producing error values and as a safe fallback. return fp_pow_base(x, y); } #endif //========================================================================= // Elementary (atom) parsing functions //========================================================================= namespace { const unsigned FP_ParamGuardMask = 1U << (sizeof(unsigned) * 8u - 1u); // ^ This mask is used to prevent cFetch/other opcode's parameters // from being confused into opcodes or variable indices within the // bytecode optimizer. Because the way it is tested in bytecoderules.dat // for speed reasons, it must also be the sign-bit of the "int" datatype. // Perhaps an "assert(IsVarOpcode(X | FP_ParamGuardMask) == false)" // might be justified to put somewhere in the code, just in case? /* Reads an UTF8-encoded sequence which forms a valid identifier name from the given input string and returns its length. If bit 31 is set, the return value also contains the internal function opcode (defined in fptypes.hh) that matches the name. */ unsigned readIdentifierCommon(const char* input) { /* Assuming unsigned = 32 bits: 76543210 76543210 76543210 76543210 Return value if built-in function: 1PPPPPPP PPPPPPPP LLLLLLLL LLLLLLLL P = function opcode (15 bits) L = function name length (16 bits) Return value if not built-in function: 0LLLLLLL LLLLLLLL LLLLLLLL LLLLLLLL L = identifier length (31 bits) If unsigned has more than 32 bits, the other higher order bits are to be assumed zero. */ #include "extrasrc/fp_identifier_parser.inc" return 0; } template inline unsigned readIdentifier(const char* input) { const unsigned value = readIdentifierCommon(input); if( (value & 0x80000000U) != 0) // Function? { // Verify that the function actually exists for this datatype if(IsIntType::result && !Functions[(value >> 16) & 0x7FFF].okForInt()) { // If it does not exist, return it as an identifier instead return value & 0xFFFFu; } if(!IsComplexType::result && Functions[(value >> 16) & 0x7FFF].complexOnly()) { // If it does not exist, return it as an identifier instead return value & 0xFFFFu; } } return value; } // Returns true if the entire string is a valid identifier template bool containsOnlyValidIdentifierChars(const std::string& name) { if(name.empty()) return false; return readIdentifier(name.c_str()) == (unsigned) name.size(); } // ----------------------------------------------------------------------- // Wrappers for strto... functions // ----------------------------------------------------------------------- template inline Value_t fp_parseLiteral(const char* str, char** endptr) { return std::strtod(str, endptr); } #if defined(FP_USE_STRTOLD) || defined(FP_SUPPORT_CPLUSPLUS11_MATH_FUNCS) template<> inline long double fp_parseLiteral(const char* str, char** endptr) { using namespace std; // Just in case strtold() is not inside std:: return strtold(str, endptr); } #endif #ifdef FP_SUPPORT_LONG_INT_TYPE template<> inline long fp_parseLiteral(const char* str, char** endptr) { return std::strtol(str, endptr, 10); } #endif #ifdef FP_SUPPORT_COMPLEX_NUMBERS template inline std::complex fp_parseComplexLiteral(const char* str, char** endptr) { T result = fp_parseLiteral (str,endptr); const char* end = *endptr; if( (*end == 'i' || *end == 'I') && !std::isalnum(end[1]) ) { ++*endptr; return std::complex (T(), result); } return std::complex (result, T()); } #endif #ifdef FP_SUPPORT_COMPLEX_DOUBLE_TYPE template<> inline std::complex fp_parseLiteral > (const char* str, char** endptr) { return fp_parseComplexLiteral (str,endptr); } #endif #ifdef FP_SUPPORT_COMPLEX_FLOAT_TYPE template<> inline std::complex fp_parseLiteral > (const char* str, char** endptr) { return fp_parseComplexLiteral (str,endptr); } #endif #ifdef FP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE template<> inline std::complex fp_parseLiteral > (const char* str, char** endptr) { return fp_parseComplexLiteral (str,endptr); } #endif // ----------------------------------------------------------------------- // Hexadecimal floating point literal parsing // ----------------------------------------------------------------------- inline int testXdigit(unsigned c) { if((c-'0') < 10u) return c&15; // 0..9 if(((c|0x20)-'a') < 6u) return 9+(c&15); // A..F or a..f return -1; // Not a hex digit } template inline void addXdigit(elem_t* buffer, unsigned nibble) { for(unsigned p=0; p> (elem_t)(limb_bits-4) ); buffer[p] = (buffer[p] << 4) | nibble; nibble = carry; } } template Value_t parseHexLiteral(const char* str, char** endptr) { const unsigned bits_per_char = 8; const int MantissaBits = std::numeric_limits::radix == 2 ? std::numeric_limits::digits : (((sizeof(Value_t) * bits_per_char) &~ 3) - 4); typedef unsigned long elem_t; const int ExtraMantissaBits = 4 + ((MantissaBits+3)&~3); // Store one digit more for correct rounding const unsigned limb_bits = sizeof(elem_t) * bits_per_char; const unsigned n_limbs = (ExtraMantissaBits + limb_bits-1) / limb_bits; elem_t mantissa_buffer[n_limbs] = { 0 }; int n_mantissa_bits = 0; // Track the number of bits int exponent = 0; // The exponent that will be used to multiply the mantissa // Read integer portion while(true) { int xdigit = testXdigit(*str); if(xdigit < 0) break; addXdigit (mantissa_buffer, xdigit); ++str; n_mantissa_bits += 4; if(n_mantissa_bits >= ExtraMantissaBits) { // Exhausted the precision. Parse the rest (until exponent) // normally but ignore the actual digits. for(; testXdigit(*str) >= 0; ++str) exponent += 4; // Read but ignore decimals if(*str == '.') for(++str; testXdigit(*str) >= 0; ++str) {} goto read_exponent; } } // Read decimals if(*str == '.') for(++str; ; ) { int xdigit = testXdigit(*str); if(xdigit < 0) break; addXdigit (mantissa_buffer, xdigit); ++str; exponent -= 4; n_mantissa_bits += 4; if(n_mantissa_bits >= ExtraMantissaBits) { // Exhausted the precision. Skip the rest // of the decimals, until the exponent. while(testXdigit(*str) >= 0) ++str; break; } } // Read exponent read_exponent: if(*str == 'p' || *str == 'P') { const char* str2 = str+1; long p_exponent = strtol(str2, const_cast (&str2), 10); if(str2 != str+1 && p_exponent == (long)(int)p_exponent) { exponent += (int)p_exponent; str = str2; } } if(endptr) *endptr = const_cast (str); Value_t result = std::ldexp(Value_t(mantissa_buffer[0]), exponent); for(unsigned p=1; p long parseHexLiteral(const char* str, char** endptr) { return std::strtol(str, endptr, 16); } #endif #ifdef FP_SUPPORT_COMPLEX_DOUBLE_TYPE template<> std::complex parseHexLiteral >(const char* str, char** endptr) { return parseHexLiteral (str, endptr); } #endif #ifdef FP_SUPPORT_COMPLEX_FLOAT_TYPE template<> std::complex parseHexLiteral >(const char* str, char** endptr) { return parseHexLiteral (str, endptr); } #endif #ifdef FP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE template<> std::complex parseHexLiteral >(const char* str, char** endptr) { return parseHexLiteral (str, endptr); } #endif } //========================================================================= // Utility functions //========================================================================= namespace { // ----------------------------------------------------------------------- // Add a new identifier to the specified identifier map // ----------------------------------------------------------------------- // Return value will be false if the name already existed template bool addNewNameData(NamePtrsMap& namePtrs, std::pair >& newName, bool isVar) { typename NamePtrsMap::iterator nameIter = namePtrs.lower_bound(newName.first); if(nameIter != namePtrs.end() && newName.first == nameIter->first) { // redefining a var is not allowed. if(isVar) return false; // redefining other tokens is allowed, if the type stays the same. if(nameIter->second.type != newName.second.type) return false; // update the data nameIter->second = newName.second; return true; } if(!isVar) { // Allocate a copy of the name (pointer stored in the map key) // However, for VARIABLEs, the pointer points to VariableString, // which is managed separately. Thusly, only done when !IsVar. char* namebuf = new char[newName.first.nameLength]; memcpy(namebuf, newName.first.name, newName.first.nameLength); newName.first.name = namebuf; } namePtrs.insert(nameIter, newName); return true; } } //========================================================================= // Data struct implementation //========================================================================= template FunctionParserBase::Data::Data(): mReferenceCounter(1), mDelimiterChar(0), mParseErrorType(NO_FUNCTION_PARSED_YET), mEvalErrorType(0), mUseDegreeConversion(false), mErrorLocation(0), mVariablesAmount(0), mStackSize(0) {} template FunctionParserBase::Data::Data(const Data& rhs): mReferenceCounter(0), mDelimiterChar(rhs.mDelimiterChar), mParseErrorType(rhs.mParseErrorType), mEvalErrorType(rhs.mEvalErrorType), mUseDegreeConversion(rhs.mUseDegreeConversion), mErrorLocation(rhs.mErrorLocation), mVariablesAmount(rhs.mVariablesAmount), mVariablesString(rhs.mVariablesString), mNamePtrs(), mFuncPtrs(rhs.mFuncPtrs), mFuncParsers(rhs.mFuncParsers), mByteCode(rhs.mByteCode), mImmed(rhs.mImmed), #ifndef FP_USE_THREAD_SAFE_EVAL mStack(rhs.mStackSize), #endif mStackSize(rhs.mStackSize) { for(typename NamePtrsMap::const_iterator i = rhs.mNamePtrs.begin(); i != rhs.mNamePtrs.end(); ++i) { if(i->second.type == NameData::VARIABLE) { const std::size_t variableStringOffset = i->first.name - rhs.mVariablesString.c_str(); std::pair > tmp (NamePtr(&mVariablesString[variableStringOffset], i->first.nameLength), i->second); mNamePtrs.insert(mNamePtrs.end(), tmp); } else { std::pair > tmp (NamePtr(new char[i->first.nameLength], i->first.nameLength), i->second ); memcpy(const_cast(tmp.first.name), i->first.name, tmp.first.nameLength); mNamePtrs.insert(mNamePtrs.end(), tmp); } } } template FunctionParserBase::Data::~Data() { for(typename NamePtrsMap::iterator i = mNamePtrs.begin(); i != mNamePtrs.end(); ++i) { if(i->second.type != NameData::VARIABLE) delete[] i->first.name; } } template void FunctionParserBase::incFuncWrapperRefCount (FunctionWrapper* wrapper) { ++wrapper->mReferenceCount; } template unsigned FunctionParserBase::decFuncWrapperRefCount (FunctionWrapper* wrapper) { return --wrapper->mReferenceCount; } template FunctionParserBase::Data::FuncWrapperPtrData::FuncWrapperPtrData(): mRawFuncPtr(0), mFuncWrapperPtr(0), mParams(0) {} template FunctionParserBase::Data::FuncWrapperPtrData::~FuncWrapperPtrData() { if(mFuncWrapperPtr && FunctionParserBase::decFuncWrapperRefCount(mFuncWrapperPtr) == 0) delete mFuncWrapperPtr; } template FunctionParserBase::Data::FuncWrapperPtrData::FuncWrapperPtrData (const FuncWrapperPtrData& rhs): mRawFuncPtr(rhs.mRawFuncPtr), mFuncWrapperPtr(rhs.mFuncWrapperPtr), mParams(rhs.mParams) { if(mFuncWrapperPtr) FunctionParserBase::incFuncWrapperRefCount(mFuncWrapperPtr); } template typename FunctionParserBase::Data::FuncWrapperPtrData& FunctionParserBase::Data::FuncWrapperPtrData::operator= (const FuncWrapperPtrData& rhs) { if(&rhs != this) { if(mFuncWrapperPtr && FunctionParserBase::decFuncWrapperRefCount(mFuncWrapperPtr) == 0) delete mFuncWrapperPtr; mRawFuncPtr = rhs.mRawFuncPtr; mFuncWrapperPtr = rhs.mFuncWrapperPtr; mParams = rhs.mParams; if(mFuncWrapperPtr) FunctionParserBase::incFuncWrapperRefCount(mFuncWrapperPtr); } return *this; } //========================================================================= // FunctionParser constructors, destructor and assignment //========================================================================= template FunctionParserBase::FunctionParserBase(): mData(new Data), mStackPtr(0) { } template FunctionParserBase::~FunctionParserBase() { if(--(mData->mReferenceCounter) == 0) delete mData; } template FunctionParserBase::FunctionParserBase(const FunctionParserBase& cpy): mData(cpy.mData), mStackPtr(0) { ++(mData->mReferenceCounter); } template FunctionParserBase& FunctionParserBase::operator=(const FunctionParserBase& cpy) { if(mData != cpy.mData) { if(--(mData->mReferenceCounter) == 0) delete mData; mData = cpy.mData; ++(mData->mReferenceCounter); } return *this; } template typename FunctionParserBase::Data* FunctionParserBase::getParserData() { return mData; } template void FunctionParserBase::setDelimiterChar(char c) { mData->mDelimiterChar = c; } //--------------------------------------------------------------------------- // Copy-on-write method //--------------------------------------------------------------------------- template void FunctionParserBase::CopyOnWrite() { if(mData->mReferenceCounter > 1) { Data* oldData = mData; mData = new Data(*oldData); --(oldData->mReferenceCounter); mData->mReferenceCounter = 1; } } template void FunctionParserBase::ForceDeepCopy() { CopyOnWrite(); } //========================================================================= // Epsilon //========================================================================= template Value_t FunctionParserBase::epsilon() { return Epsilon::value; } template void FunctionParserBase::setEpsilon(Value_t value) { Epsilon::value = value; } //========================================================================= // User-defined identifier addition functions //========================================================================= template bool FunctionParserBase::AddConstant(const std::string& name, Value_t value) { if(!containsOnlyValidIdentifierChars(name)) return false; CopyOnWrite(); std::pair > newName (NamePtr(name.data(), unsigned(name.size())), NameData(NameData::CONSTANT, value)); return addNewNameData(mData->mNamePtrs, newName, false); } template bool FunctionParserBase::AddUnit(const std::string& name, Value_t value) { if(!containsOnlyValidIdentifierChars(name)) return false; CopyOnWrite(); std::pair > newName (NamePtr(name.data(), unsigned(name.size())), NameData(NameData::UNIT, value)); return addNewNameData(mData->mNamePtrs, newName, false); } template bool FunctionParserBase::AddFunction (const std::string& name, FunctionPtr ptr, unsigned paramsAmount) { if(!containsOnlyValidIdentifierChars(name)) return false; CopyOnWrite(); std::pair > newName (NamePtr(name.data(), unsigned(name.size())), NameData(NameData::FUNC_PTR, unsigned(mData->mFuncPtrs.size()))); const bool success = addNewNameData(mData->mNamePtrs, newName, false); if(success) { mData->mFuncPtrs.push_back(typename Data::FuncWrapperPtrData()); mData->mFuncPtrs.back().mRawFuncPtr = ptr; mData->mFuncPtrs.back().mParams = paramsAmount; } return success; } template bool FunctionParserBase::addFunctionWrapperPtr (const std::string& name, FunctionWrapper* wrapper, unsigned paramsAmount) { if(!AddFunction(name, FunctionPtr(0), paramsAmount)) return false; mData->mFuncPtrs.back().mFuncWrapperPtr = wrapper; return true; } template typename FunctionParserBase::FunctionWrapper* FunctionParserBase::GetFunctionWrapper(const std::string& name) { CopyOnWrite(); NamePtr namePtr(name.data(), unsigned(name.size())); typename NamePtrsMap::iterator nameIter = mData->mNamePtrs.find(namePtr); if(nameIter != mData->mNamePtrs.end() && nameIter->second.type == NameData::FUNC_PTR) { return mData->mFuncPtrs[nameIter->second.index].mFuncWrapperPtr; } return 0; } template bool FunctionParserBase::CheckRecursiveLinking (const FunctionParserBase* fp) const { if(fp == this) return true; for(unsigned i = 0; i < fp->mData->mFuncParsers.size(); ++i) if(CheckRecursiveLinking(fp->mData->mFuncParsers[i].mParserPtr)) return true; return false; } template bool FunctionParserBase::AddFunction(const std::string& name, FunctionParserBase& fp) { if(!containsOnlyValidIdentifierChars(name) || CheckRecursiveLinking(&fp)) return false; CopyOnWrite(); std::pair > newName (NamePtr(name.data(), unsigned(name.size())), NameData(NameData::PARSER_PTR, unsigned(mData->mFuncParsers.size()))); const bool success = addNewNameData(mData->mNamePtrs, newName, false); if(success) { mData->mFuncParsers.push_back(typename Data::FuncParserPtrData()); mData->mFuncParsers.back().mParserPtr = &fp; mData->mFuncParsers.back().mParams = fp.mData->mVariablesAmount; } return success; } template bool FunctionParserBase::RemoveIdentifier(const std::string& name) { CopyOnWrite(); NamePtr namePtr(name.data(), unsigned(name.size())); typename NamePtrsMap::iterator nameIter = mData->mNamePtrs.find(namePtr); if(nameIter != mData->mNamePtrs.end()) { if(nameIter->second.type == NameData::VARIABLE) { // Illegal attempt to delete variables return false; } delete[] nameIter->first.name; mData->mNamePtrs.erase(nameIter); return true; } return false; } //========================================================================= // Function parsing //========================================================================= namespace { // Error messages returned by ErrorMsg(): const char* const ParseErrorMessage[]= { "Syntax error", // 0 "Mismatched parenthesis", // 1 "Missing ')'", // 2 "Empty parentheses", // 3 "Syntax error: Operator expected", // 4 "Not enough memory", // 5 "An unexpected error occurred. Please make a full bug report " "to the author", // 6 "Syntax error in parameter 'Vars' given to " "FunctionParser::Parse()", // 7 "Illegal number of parameters to function", // 8 "Syntax error: Premature end of string", // 9 "Syntax error: Expecting ( after function", // 10 "Syntax error: Unknown identifier", // 11 "(No function has been parsed yet)", "" }; template inline typename FunctionParserBase::ParseErrorType noCommaError(char c) { return c == ')' ? FunctionParserBase::ILL_PARAMS_AMOUNT : FunctionParserBase::SYNTAX_ERROR; } template inline typename FunctionParserBase::ParseErrorType noParenthError(char c) { return c == ',' ? FunctionParserBase::ILL_PARAMS_AMOUNT : FunctionParserBase::MISSING_PARENTH; } template struct IntLiteralMask { enum { mask = // ( 1UL << ('-'-offset)) | (0x3FFUL << ('0'-offset)) }; /* 0x3FF = 10 bits worth "1" */ // Note: If you change fparser to support negative numbers parsing // (as opposed to parsing them as cNeg followed by literal), // enable the '-' line above, and change the offset value // in BeginsLiteral() to '-' instead of '.'. }; template struct LiteralMask { enum { mask = ( 1UL << ('.'-offset)) | IntLiteralMask::mask }; }; #ifdef FP_SUPPORT_LONG_INT_TYPE template struct LiteralMask: public IntLiteralMask { }; #endif #ifdef FP_SUPPORT_GMP_INT_TYPE template struct LiteralMask: public IntLiteralMask { }; #endif template struct SimpleSpaceMask { enum { mask = (1UL << ('\r'-offset)) | (1UL << ('\n'-offset)) | (1UL << ('\v'-offset)) | (1UL << ('\t'-offset)) | (1UL << (' ' -offset)) }; }; template inline bool BeginsLiteral(unsigned byte) { enum { n = sizeof(unsigned long)>=8 ? 0 : '.' }; byte -= n; if(byte > (unsigned char)('9'-n)) return false; unsigned long shifted = 1UL << byte; const unsigned long mask = LiteralMask::mask; return (mask & shifted) != 0; } template inline void SkipSpace(CharPtr& function) { /* Space characters in unicode: U+0020 SPACE Depends on font, often adjusted (see below) U+00A0 NO-BREAK SPACE As a space, but often not adjusted U+2000 EN QUAD 1 en (= 1/2 em) U+2001 EM QUAD 1 em (nominally, the height of the font) U+2002 EN SPACE 1 en (= 1/2 em) U+2003 EM SPACE 1 em U+2004 THREE-PER-EM SPACE 1/3 em U+2005 FOUR-PER-EM SPACE 1/4 em U+2006 SIX-PER-EM SPACE 1/6 em U+2007 FIGURE SPACE Tabular width, the width of digits U+2008 PUNCTUATION SPACE The width of a period . U+2009 THIN SPACE 1/5 em (or sometimes 1/6 em) U+200A HAIR SPACE Narrower than THIN SPACE U+200B ZERO WIDTH SPACE Nominally no width, but may expand U+202F NARROW NO-BREAK SPACE Narrower than NO-BREAK SPACE (or SPACE) U+205F MEDIUM MATHEMATICAL SPACE 4/18 em U+3000 IDEOGRAPHIC SPACE The width of ideographic (CJK) characters. Also: U+000A \n U+000D \r U+0009 \t U+000B \v As UTF-8 sequences: 09 0A 0B 0D 20 C2 A0 E2 80 80-8B E2 80 AF E2 81 9F E3 80 80 */ while(true) { enum { n = sizeof(unsigned long)>=8 ? 0 : '\t' }; typedef signed char schar; unsigned byte = (unsigned char)*function; byte -= n; // ^Note: values smaller than n intentionally become // big values here due to integer wrap. The // comparison below thus excludes them, making // the effective range 0x09..0x20 (32-bit) // or 0x00..0x20 (64-bit) within the if-clause. if(byte <= (unsigned char)(' '-n)) { unsigned long shifted = 1UL << byte; const unsigned long mask = SimpleSpaceMask::mask; if(mask & shifted) { ++function; continue; } // \r, \n, \t, \v and space break; } if(likely(byte < 0xC2-n)) break; if(byte == 0xC2-n && function[1] == char(0xA0)) { function += 2; continue; } // U+00A0 if(byte == 0xE3-n && function[1] == char(0x80) && function[2] == char(0x80)) { function += 3; continue; } // U+3000 if(byte == 0xE2-n) { if(function[1] == char(0x81)) { if(function[2] != char(0x9F)) break; function += 3; // U+205F continue; } if(function[1] == char(0x80)) if(function[2] == char(0xAF) || // U+202F schar(function[2]) <= schar(0x8B) // U+2000..U+200B ) { function += 3; continue; } } break; } // while(true) } // SkipSpace(CharPtr& function) } // --------------------------------------------------------------------------- // Return parse error message // --------------------------------------------------------------------------- template const char* FunctionParserBase::ErrorMsg() const { return ParseErrorMessage[mData->mParseErrorType]; } template typename FunctionParserBase::ParseErrorType FunctionParserBase::GetParseErrorType() const { return mData->mParseErrorType; } template int FunctionParserBase::EvalError() const { return mData->mEvalErrorType; } // --------------------------------------------------------------------------- // Parse variables // --------------------------------------------------------------------------- template bool FunctionParserBase::ParseVariables (const std::string& inputVarString) { if(mData->mVariablesString == inputVarString) return true; /* Delete existing variables from mNamePtrs */ for(typename NamePtrsMap::iterator i = mData->mNamePtrs.begin(); i != mData->mNamePtrs.end(); ) { if(i->second.type == NameData::VARIABLE) { typename NamePtrsMap::iterator j (i); ++i; mData->mNamePtrs.erase(j); } else ++i; } mData->mVariablesString = inputVarString; const std::string& vars = mData->mVariablesString; const unsigned len = unsigned(vars.size()); unsigned varNumber = VarBegin; const char* beginPtr = vars.c_str(); const char* finalPtr = beginPtr + len; while(beginPtr < finalPtr) { SkipSpace(beginPtr); unsigned nameLength = readIdentifier(beginPtr); if(nameLength == 0 || (nameLength & 0x80000000U)) return false; const char* endPtr = beginPtr + nameLength; SkipSpace(endPtr); if(endPtr != finalPtr && *endPtr != ',') return false; std::pair > newName (NamePtr(beginPtr, nameLength), NameData(NameData::VARIABLE, varNumber++)); if(!addNewNameData(mData->mNamePtrs, newName, true)) { return false; } beginPtr = endPtr + 1; } mData->mVariablesAmount = varNumber - VarBegin; return true; } // --------------------------------------------------------------------------- // Parse() public interface functions // --------------------------------------------------------------------------- template int FunctionParserBase::Parse(const char* Function, const std::string& Vars, bool useDegrees) { CopyOnWrite(); if(!ParseVariables(Vars)) { mData->mParseErrorType = INVALID_VARS; return int(strlen(Function)); } return ParseFunction(Function, useDegrees); } template int FunctionParserBase::Parse(const std::string& Function, const std::string& Vars, bool useDegrees) { CopyOnWrite(); if(!ParseVariables(Vars)) { mData->mParseErrorType = INVALID_VARS; return int(Function.size()); } return ParseFunction(Function.c_str(), useDegrees); } // --------------------------------------------------------------------------- // Main parsing function // --------------------------------------------------------------------------- template int FunctionParserBase::ParseFunction(const char* function, bool useDegrees) { mData->mUseDegreeConversion = useDegrees; mData->mParseErrorType = FP_NO_ERROR; mData->mInlineVarNames.clear(); mData->mByteCode.clear(); mData->mByteCode.reserve(128); mData->mImmed.clear(); mData->mImmed.reserve(128); mData->mStackSize = mStackPtr = 0; mData->mHasByteCodeFlags = false; const char* ptr = Compile(function); mData->mInlineVarNames.clear(); if(mData->mHasByteCodeFlags) { for(unsigned i = unsigned(mData->mByteCode.size()); i-- > 0; ) mData->mByteCode[i] &= ~FP_ParamGuardMask; } if(mData->mParseErrorType != FP_NO_ERROR) return int(mData->mErrorLocation - function); assert(ptr); // Should never be null at this point. It's a bug otherwise. if(*ptr) { if(mData->mDelimiterChar == 0 || *ptr != mData->mDelimiterChar) mData->mParseErrorType = EXPECT_OPERATOR; return int(ptr - function); } #ifndef FP_USE_THREAD_SAFE_EVAL mData->mStack.resize(mData->mStackSize); #endif return -1; } //========================================================================= // Parsing and bytecode compiling functions //========================================================================= template inline const char* FunctionParserBase::SetErrorType(ParseErrorType t, const char* pos) { mData->mParseErrorType = t; mData->mErrorLocation = pos; return 0; } template inline void FunctionParserBase::incStackPtr() { if(++mStackPtr > mData->mStackSize) ++(mData->mStackSize); } namespace { const unsigned char powi_factor_table[128] = { 0,1,0,0,0,0,0,0, 0, 0,0,0,0,0,0,3,/* 0 - 15 */ 0,0,0,0,0,0,0,0, 0, 5,0,3,0,0,3,0,/* 16 - 31 */ 0,0,0,0,0,0,0,3, 0, 0,0,0,0,5,0,0,/* 32 - 47 */ 0,0,5,3,0,0,3,5, 0, 3,0,0,3,0,0,3,/* 48 - 63 */ 0,0,0,0,0,0,0,0, 0, 0,0,3,0,0,3,0,/* 64 - 79 */ 0,9,0,0,0,5,0,3, 0, 0,5,7,0,0,0,5,/* 80 - 95 */ 0,0,0,3,5,0,3,0, 0, 3,0,0,3,0,5,3,/* 96 - 111 */ 0,0,3,5,0,9,0,7, 3,11,0,3,0,5,3,0,/* 112 - 127 */ }; inline int get_powi_factor(long abs_int_exponent) { if(abs_int_exponent >= int(sizeof(powi_factor_table))) return 0; return powi_factor_table[abs_int_exponent]; } #if 0 int EstimatePowiComplexity(int abs_int_exponent) { int cost = 0; while(abs_int_exponent > 1) { int factor = get_powi_factor(abs_int_exponent); if(factor) { cost += EstimatePowiComplexity(factor); abs_int_exponent /= factor; continue; } if(!(abs_int_exponent & 1)) { abs_int_exponent /= 2; cost += 3; // sqr } else { cost += 4; // dup+mul abs_int_exponent -= 1; } } return cost; } #endif bool IsEligibleIntPowiExponent(long int_exponent) { if(int_exponent == 0) return false; long abs_int_exponent = int_exponent; #if 0 int cost = 0; if(abs_int_exponent < 0) { cost += 11; abs_int_exponent = -abs_int_exponent; } cost += EstimatePowiComplexity(abs_int_exponent); return cost < (10*3 + 4*4); #else if(abs_int_exponent < 0) abs_int_exponent = -abs_int_exponent; return (abs_int_exponent >= 1) && (abs_int_exponent <= 46 || (abs_int_exponent <= 1024 && (abs_int_exponent & (abs_int_exponent - 1)) == 0)); #endif } /* Needed by fp_opcode_add.inc if tracing is enabled */ template std::string findName(const NamePtrsMap& nameMap, unsigned index, typename NameData::DataType type) { for(typename NamePtrsMap::const_iterator iter = nameMap.begin(); iter != nameMap.end(); ++iter) { if(iter->second.type == type && iter->second.index == index) return std::string(iter->first.name, iter->first.name + iter->first.nameLength); } return "?"; } } template inline void FunctionParserBase::AddImmedOpcode(Value_t value) { mData->mImmed.push_back(value); mData->mByteCode.push_back(cImmed); } template inline void FunctionParserBase::CompilePowi(long abs_int_exponent) { int num_muls=0; while(abs_int_exponent > 1) { long factor = get_powi_factor(abs_int_exponent); if(factor) { CompilePowi(factor); abs_int_exponent /= factor; continue; } if(!(abs_int_exponent & 1)) { abs_int_exponent /= 2; mData->mByteCode.push_back(cSqr); // ^ Don't put AddFunctionOpcode here, // it would slow down a great deal. } else { mData->mByteCode.push_back(cDup); incStackPtr(); abs_int_exponent -= 1; ++num_muls; } } if(num_muls > 0) { mData->mByteCode.resize(mData->mByteCode.size()+num_muls, cMul); mStackPtr -= num_muls; } } template inline bool FunctionParserBase::TryCompilePowi(Value_t original_immed) { Value_t changed_immed = original_immed; for(int sqrt_count=0; /**/; ++sqrt_count) { long int_exponent = makeLongInteger(changed_immed); if(isLongInteger(changed_immed) && IsEligibleIntPowiExponent(int_exponent)) { long abs_int_exponent = int_exponent; if(abs_int_exponent < 0) abs_int_exponent = -abs_int_exponent; mData->mImmed.pop_back(); mData->mByteCode.pop_back(); --mStackPtr; // ^Though the above is accounted for by the procedure // that generates cPow, we need it for correct cFetch // indexes in CompilePowi(). while(sqrt_count > 0) { int opcode = cSqrt; if(sqrt_count == 1 && int_exponent < 0) { opcode = cRSqrt; int_exponent = -int_exponent; } mData->mByteCode.push_back(opcode); --sqrt_count; } if((abs_int_exponent & 1) == 0) { // This special rule fixes the optimization // shortcoming of (-x)^2 with minimal overhead. AddFunctionOpcode(cSqr); abs_int_exponent >>= 1; } CompilePowi(abs_int_exponent); if(int_exponent < 0) mData->mByteCode.push_back(cInv); ++mStackPtr; // Needed because cPow adding will assume this. return true; } if(sqrt_count >= 4) break; changed_immed += changed_immed; } // When we don't know whether x >= 0, we still know that // x^y can be safely converted into exp(y * log(x)) // when y is _not_ integer, because we know that x >= 0. // Otherwise either expression will give a NaN. if(/*!isInteger(original_immed) ||*/ IsNeverNegativeValueOpcode(mData->mByteCode[mData->mByteCode.size()-2])) { mData->mImmed.pop_back(); mData->mByteCode.pop_back(); //--mStackPtr; - accounted for by the procedure that generates cPow AddFunctionOpcode(cLog); AddImmedOpcode(original_immed); //incStackPtr(); - this and the next are redundant because... AddFunctionOpcode(cMul); //--mStackPtr; - ...because the cImmed was popped earlier. AddFunctionOpcode(cExp); return true; } return false; } //#include "fpoptimizer/opcodename.hh" // ^ needed only if FP_TRACE_BYTECODE_OPTIMIZATION() is used template inline void FunctionParserBase::AddFunctionOpcode(unsigned opcode) { #define FP_FLOAT_VERSION 1 #define FP_COMPLEX_VERSION 0 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #ifdef FP_SUPPORT_LONG_INT_TYPE template<> inline void FunctionParserBase::AddFunctionOpcode(unsigned opcode) { typedef long Value_t; #define FP_FLOAT_VERSION 0 #define FP_COMPLEX_VERSION 0 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #endif #ifdef FP_SUPPORT_GMP_INT_TYPE template<> inline void FunctionParserBase::AddFunctionOpcode(unsigned opcode) { typedef GmpInt Value_t; #define FP_FLOAT_VERSION 0 #define FP_COMPLEX_VERSION 0 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #endif #ifdef FP_SUPPORT_COMPLEX_DOUBLE_TYPE template<> inline void FunctionParserBase >::AddFunctionOpcode(unsigned opcode) { typedef std::complex Value_t; #define FP_FLOAT_VERSION 1 #define FP_COMPLEX_VERSION 1 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #endif #ifdef FP_SUPPORT_COMPLEX_FLOAT_TYPE template<> inline void FunctionParserBase >::AddFunctionOpcode(unsigned opcode) { typedef std::complex Value_t; #define FP_FLOAT_VERSION 1 #define FP_COMPLEX_VERSION 1 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #endif #ifdef FP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE template<> inline void FunctionParserBase >::AddFunctionOpcode(unsigned opcode) { typedef std::complex Value_t; #define FP_FLOAT_VERSION 1 #define FP_COMPLEX_VERSION 1 #include "extrasrc/fp_opcode_add.inc" #undef FP_COMPLEX_VERSION #undef FP_FLOAT_VERSION } #endif template unsigned FunctionParserBase::ParseIdentifier(const char* function) { return readIdentifier(function); } template std::pair FunctionParserBase::ParseLiteral(const char* function) { char* endptr; #if 0 /* Profile the hex literal parser */ if(function[0]=='0' && function[1]=='x') { // Parse hexadecimal literal if fp_parseLiteral didn't already Value_t val = parseHexLiteral(function+2, &endptr); if(endptr == function+2) return std::pair (function, Value_t()); return std::pair (endptr, val); } #endif Value_t val = fp_parseLiteral(function, &endptr); if(endptr == function+1 && function[0] == '0' && function[1] == 'x') { // Parse hexadecimal literal if fp_parseLiteral didn't already val = parseHexLiteral(function+2, &endptr); if(endptr == function+2) return std::pair (function, Value_t()); } else if(endptr == function) return std::pair (function, Value_t()); return std::pair (endptr, val); } #ifdef FP_SUPPORT_MPFR_FLOAT_TYPE template<> std::pair FunctionParserBase::ParseLiteral(const char* function) { char* endPtr; const MpfrFloat val = MpfrFloat::parseString(function, &endPtr); if(endPtr == function) return std::pair (function, MpfrFloat()); return std::pair (endPtr, val); } #endif #ifdef FP_SUPPORT_GMP_INT_TYPE template<> std::pair FunctionParserBase::ParseLiteral(const char* function) { char* endPtr; const GmpInt val = GmpInt::parseString(function, &endPtr); if(endPtr == function) return std::pair (function, GmpInt()); return std::pair (endPtr, val); } #endif template inline const char* FunctionParserBase::CompileLiteral(const char* function) { std::pair result = ParseLiteral(function); if(result.first == function) return SetErrorType(SYNTAX_ERROR, result.first); AddImmedOpcode(result.second); incStackPtr(); SkipSpace(result.first); return result.first; } template const char* FunctionParserBase::CompileIf(const char* function) { if(*function != '(') return SetErrorType(EXPECT_PARENTH_FUNC, function); function = CompileExpression(function+1); if(!function) return 0; if(*function != ',') return SetErrorType(noCommaError(*function), function); OPCODE opcode = cIf; if(mData->mByteCode.back() == cNotNot) mData->mByteCode.pop_back(); if(IsNeverNegativeValueOpcode(mData->mByteCode.back())) { // If we know that the condition to be tested is always // a positive value (such as when produced by "x= 0.5, // cAbsIf simply tests whether cond >= 0.5. opcode = cAbsIf; } mData->mByteCode.push_back(opcode); const unsigned curByteCodeSize = unsigned(mData->mByteCode.size()); PushOpcodeParam(0); // Jump index; to be set later PushOpcodeParam (0); // Immed jump index; to be set later --mStackPtr; function = CompileExpression(function + 1); if(!function) return 0; if(*function != ',') return SetErrorType(noCommaError(*function), function); mData->mByteCode.push_back(cJump); const unsigned curByteCodeSize2 = unsigned(mData->mByteCode.size()); const unsigned curImmedSize2 = unsigned(mData->mImmed.size()); PushOpcodeParam(0); // Jump index; to be set later PushOpcodeParam (0); // Immed jump index; to be set later --mStackPtr; function = CompileExpression(function + 1); if(!function) return 0; if(*function != ')') return SetErrorType(noParenthError(*function), function); PutOpcodeParamAt ( mData->mByteCode.back(), unsigned(mData->mByteCode.size()-1) ); // ^Necessary for guarding against if(x,1,2)+1 being changed // into if(x,1,3) by fp_opcode_add.inc // Set jump indices PutOpcodeParamAt( curByteCodeSize2+1, curByteCodeSize ); PutOpcodeParamAt( curImmedSize2, curByteCodeSize+1 ); PutOpcodeParamAt( unsigned(mData->mByteCode.size())-1, curByteCodeSize2); PutOpcodeParamAt( unsigned(mData->mImmed.size()), curByteCodeSize2+1); ++function; SkipSpace(function); return function; } template const char* FunctionParserBase::CompileFunctionParams (const char* function, unsigned requiredParams) { if(*function != '(') return SetErrorType(EXPECT_PARENTH_FUNC, function); if(requiredParams > 0) { const char* function_end = CompileExpression(function+1); if(!function_end) { // If an error occurred, verify whether it was caused by () ++function; SkipSpace(function); if(*function == ')') return SetErrorType(ILL_PARAMS_AMOUNT, function); // Not caused by (), use the error message given by CompileExpression() return 0; } function = function_end; for(unsigned i = 1; i < requiredParams; ++i) { if(*function != ',') return SetErrorType(noCommaError(*function), function); function = CompileExpression(function+1); if(!function) return 0; } // No need for incStackPtr() because each parse parameter calls it mStackPtr -= requiredParams-1; } else { incStackPtr(); // return value of function is pushed onto the stack ++function; SkipSpace(function); } if(*function != ')') return SetErrorType(noParenthError(*function), function); ++function; SkipSpace(function); return function; } template const char* FunctionParserBase::CompileElement(const char* function) { if(BeginsLiteral( (unsigned char) *function)) return CompileLiteral(function); unsigned nameLength = readIdentifier(function); if(nameLength == 0) { // No identifier found if(*function == '(') return CompileParenthesis(function); if(*function == ')') return SetErrorType(MISM_PARENTH, function); return SetErrorType(SYNTAX_ERROR, function); } // Function, variable or constant if(nameLength & 0x80000000U) // Function { OPCODE func_opcode = OPCODE( (nameLength >> 16) & 0x7FFF ); return CompileFunction(function + (nameLength & 0xFFFF), func_opcode); } NamePtr name(function, nameLength); const char* endPtr = function + nameLength; SkipSpace(endPtr); typename NamePtrsMap::iterator nameIter = mData->mNamePtrs.find(name); if(nameIter == mData->mNamePtrs.end()) { // Check if it's an inline variable: for(typename Data::InlineVarNamesContainer::reverse_iterator iter = mData->mInlineVarNames.rbegin(); iter != mData->mInlineVarNames.rend(); ++iter) { if(name == iter->mName) { if( iter->mFetchIndex+1 == mStackPtr) { mData->mByteCode.push_back(cDup); } else { mData->mByteCode.push_back(cFetch); PushOpcodeParam(iter->mFetchIndex); } incStackPtr(); return endPtr; } } return SetErrorType(UNKNOWN_IDENTIFIER, function); } const NameData* nameData = &nameIter->second; switch(nameData->type) { case NameData::VARIABLE: // is variable if(unlikely(!mData->mByteCode.empty() && mData->mByteCode.back() == nameData->index)) mData->mByteCode.push_back(cDup); else mData->mByteCode.push_back(nameData->index); incStackPtr(); return endPtr; case NameData::CONSTANT: // is constant AddImmedOpcode(nameData->value); incStackPtr(); return endPtr; case NameData::UNIT: // is unit (error if appears here) break; case NameData::FUNC_PTR: // is C++ function function = CompileFunctionParams (endPtr, mData->mFuncPtrs[nameData->index].mParams); //if(!function) return 0; mData->mByteCode.push_back(cFCall); PushOpcodeParam(nameData->index); return function; case NameData::PARSER_PTR: // is FunctionParser function = CompileFunctionParams (endPtr, mData->mFuncParsers[nameData->index].mParams); //if(!function) return 0; mData->mByteCode.push_back(cPCall); PushOpcodeParam(nameData->index); return function; } // When it's an unit (or unrecognized type): return SetErrorType(SYNTAX_ERROR, function); } template inline const char* FunctionParserBase::CompileFunction (const char* function, unsigned func_opcode) { SkipSpace(function); const FuncDefinition& funcDef = Functions[func_opcode]; if(func_opcode == cIf) // "if" is a special case return CompileIf(function); unsigned requiredParams = funcDef.params; function = CompileFunctionParams(function, requiredParams); if(!function) return 0; if(mData->mUseDegreeConversion) { if(funcDef.flags & FuncDefinition::AngleIn) AddFunctionOpcode(cRad); AddFunctionOpcode(func_opcode); if(funcDef.flags & FuncDefinition::AngleOut) AddFunctionOpcode(cDeg); } else { AddFunctionOpcode(func_opcode); } return function; } template inline const char* FunctionParserBase::CompileParenthesis(const char* function) { ++function; // Skip '(' SkipSpace(function); if(*function == ')') return SetErrorType(EMPTY_PARENTH, function); function = CompileExpression(function); if(!function) return 0; if(*function != ')') return SetErrorType(MISSING_PARENTH, function); ++function; // Skip ')' SkipSpace(function); return function; } template const char* FunctionParserBase::CompilePossibleUnit(const char* function) { unsigned nameLength = readIdentifier(function); if(nameLength & 0x80000000U) return function; // built-in function name if(nameLength != 0) { NamePtr name(function, nameLength); typename NamePtrsMap::iterator nameIter = mData->mNamePtrs.find(name); if(nameIter != mData->mNamePtrs.end()) { const NameData* nameData = &nameIter->second; if(nameData->type == NameData::UNIT) { AddImmedOpcode(nameData->value); incStackPtr(); AddFunctionOpcode(cMul); --mStackPtr; const char* endPtr = function + nameLength; SkipSpace(endPtr); return endPtr; } } } return function; } template inline const char* FunctionParserBase::CompilePow(const char* function) { function = CompileElement(function); if(!function) return 0; function = CompilePossibleUnit(function); if(*function == '^') { ++function; SkipSpace(function); unsigned op = cPow; if(mData->mByteCode.back() == cImmed) { if(mData->mImmed.back() == fp_const_e()) { op = cExp; mData->mByteCode.pop_back(); mData->mImmed.pop_back(); --mStackPtr; } else if(mData->mImmed.back() == Value_t(2)) { op = cExp2; mData->mByteCode.pop_back(); mData->mImmed.pop_back(); --mStackPtr; } } function = CompileUnaryMinus(function); if(!function) return 0; // add opcode AddFunctionOpcode(op); if(op == cPow) --mStackPtr; } return function; } /* Currently the power operator is skipped for integral types because its usefulness with them is questionable, and in the case of GmpInt, for safety reasons: - With long int almost any power, except for very small ones, would overflow the result, so the usefulness of this is rather questionable. - With GmpInt the power operator could be easily abused to make the program run out of memory (think of a function like "10^10^10^10^1000000"). */ #ifdef FP_SUPPORT_LONG_INT_TYPE template<> inline const char* FunctionParserBase::CompilePow(const char* function) { function = CompileElement(function); if(!function) return 0; return CompilePossibleUnit(function); } #endif #ifdef FP_SUPPORT_GMP_INT_TYPE template<> inline const char* FunctionParserBase::CompilePow(const char* function) { function = CompileElement(function); if(!function) return 0; return CompilePossibleUnit(function); } #endif template inline const char* FunctionParserBase::CompileUnaryMinus(const char* function) { char op = *function; switch(op) { case '-': case '!': ++function; SkipSpace(function); function = CompileUnaryMinus(function); if(!function) return 0; AddFunctionOpcode(op=='-' ? cNeg : cNot); return function; default: break; } return CompilePow(function); } template inline const char* FunctionParserBase::CompileMult(const char* function) { function = CompileUnaryMinus(function); if(!function) return 0; Value_t pending_immed(1); #define FP_FlushImmed(do_reset) \ if(pending_immed != Value_t(1)) \ { \ unsigned op = cMul; \ if(!IsIntType::result && mData->mByteCode.back() == cInv) \ { \ /* (...) cInv 5 cMul -> (...) 5 cRDiv */ \ /* ^ ^ | */ \ mData->mByteCode.pop_back(); \ op = cRDiv; \ } \ AddImmedOpcode(pending_immed); \ incStackPtr(); \ AddFunctionOpcode(op); \ --mStackPtr; \ if(do_reset) pending_immed = Value_t(1); \ } while(true) { char c = *function; if(c == '%') { FP_FlushImmed(true); ++function; SkipSpace(function); function = CompileUnaryMinus(function); if(!function) return 0; AddFunctionOpcode(cMod); --mStackPtr; continue; } if(c != '*' && c != '/') break; bool safe_cumulation = (c == '*' || !IsIntType::result); if(!safe_cumulation) { FP_FlushImmed(true); } ++function; SkipSpace(function); if(mData->mByteCode.back() == cImmed && (safe_cumulation || mData->mImmed.back() == Value_t(1))) { // 5 (...) cMul --> (...) ||| 5 cMul // 5 (...) cDiv --> (...) cInv ||| 5 cMul // ^ | ^ pending_immed *= mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); --mStackPtr; function = CompileUnaryMinus(function); if(!function) return 0; if(c == '/') AddFunctionOpcode(cInv); continue; } if(safe_cumulation && mData->mByteCode.back() == cMul && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) 5 cMul (...) cMul -> (:::) (...) cMul ||| 5 cMul // (:::) 5 cMul (...) cDiv -> (:::) (...) cDiv ||| 5 cMul // ^ ^ pending_immed *= mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } // cDiv is not tested here because the bytecode // optimizer will convert this kind of cDivs into cMuls. bool lhs_inverted = false; if(!IsIntType::result && c == '*' && mData->mByteCode.back() == cInv) { // (:::) cInv (...) cMul -> (:::) (...) cRDiv // (:::) cInv (...) cDiv -> (:::) (...) cMul cInv // ^ ^ | mData->mByteCode.pop_back(); lhs_inverted = true; } function = CompileUnaryMinus(function); if(!function) return 0; if(safe_cumulation && mData->mByteCode.back() == cMul && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) (...) 5 cMul cMul -> (:::) (...) cMul ||| 5 Mul // (:::) (...) 5 cMul cDiv -> (:::) (...) cDiv ||| /5 Mul // ^ ^ if(c == '*') pending_immed *= mData->mImmed.back(); else pending_immed /= mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } else if(safe_cumulation && mData->mByteCode.back() == cRDiv && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) (...) 5 cRDiv cMul -> (:::) (...) cDiv ||| 5 cMul // (:::) (...) 5 cRDiv cDiv -> (:::) (...) cMul ||| /5 cMul // ^ ^ if(c == '*') { c = '/'; pending_immed *= mData->mImmed.back(); } else { c = '*'; pending_immed /= mData->mImmed.back(); } mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } if(!lhs_inverted) // if (/x/y) was changed to /(x*y), add missing cInv { AddFunctionOpcode(c == '*' ? cMul : cDiv); --mStackPtr; } else if(c == '*') // (/x)*y -> rdiv(x,y) { AddFunctionOpcode(cRDiv); --mStackPtr; } else // (/x)/y -> /(x*y) { AddFunctionOpcode(cMul); --mStackPtr; AddFunctionOpcode(cInv); } } FP_FlushImmed(false); #undef FP_FlushImmed return function; } template inline const char* FunctionParserBase::CompileAddition(const char* function) { function = CompileMult(function); if(!function) return 0; Value_t pending_immed(0); #define FP_FlushImmed(do_reset) \ if(pending_immed != Value_t(0)) \ { \ unsigned op = cAdd; \ if(mData->mByteCode.back() == cNeg) \ { \ /* (...) cNeg 5 cAdd -> (...) 5 cRSub */ \ /* ^ ^ | */ \ mData->mByteCode.pop_back(); \ op = cRSub; \ } \ AddImmedOpcode(pending_immed); \ incStackPtr(); \ AddFunctionOpcode(op); \ --mStackPtr; \ if(do_reset) pending_immed = Value_t(0); \ } while(true) { char c = *function; if(c != '+' && c != '-') break; ++function; SkipSpace(function); if(mData->mByteCode.back() == cImmed) { // 5 (...) cAdd --> (...) ||| 5 cAdd // 5 (...) cSub --> (...) cNeg ||| 5 cAdd // ^ | ^ pending_immed += mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); --mStackPtr; function = CompileMult(function); if(!function) return 0; if(c == '-') AddFunctionOpcode(cNeg); continue; } if(mData->mByteCode.back() == cAdd && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) 5 cAdd (...) cAdd -> (:::) (...) cAdd ||| 5 cAdd // (:::) 5 cAdd (...) cSub -> (:::) (...) cSub ||| 5 cAdd // ^ ^ pending_immed += mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } // cSub is not tested here because the bytecode // optimizer will convert this kind of cSubs into cAdds. bool lhs_negated = false; if(mData->mByteCode.back() == cNeg) { // (:::) cNeg (...) cAdd -> (:::) (...) cRSub // (:::) cNeg (...) cSub -> (:::) (...) cAdd cNeg // ^ ^ | mData->mByteCode.pop_back(); lhs_negated = true; } function = CompileMult(function); if(!function) return 0; if(mData->mByteCode.back() == cAdd && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) (...) 5 cAdd cAdd -> (:::) (...) cAdd ||| 5 Add // (:::) (...) 5 cAdd cSub -> (:::) (...) cSub ||| -5 Add // ^ ^ if(c == '+') pending_immed += mData->mImmed.back(); else pending_immed -= mData->mImmed.back(); mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } else if(mData->mByteCode.back() == cRSub && mData->mByteCode[mData->mByteCode.size()-2] == cImmed) { // (:::) (...) 5 cRSub cAdd -> (:::) (...) cSub ||| 5 cAdd // (:::) (...) 5 cRSub cSub -> (:::) (...) cAdd ||| -5 cAdd // ^ ^ if(c == '+') { c = '-'; pending_immed += mData->mImmed.back(); } else { c = '+'; pending_immed -= mData->mImmed.back(); } mData->mImmed.pop_back(); mData->mByteCode.pop_back(); mData->mByteCode.pop_back(); } if(!lhs_negated) // if (-x-y) was changed to -(x+y), add missing cNeg { AddFunctionOpcode(c == '+' ? cAdd : cSub); --mStackPtr; } else if(c == '+') // (-x)+y -> rsub(x,y) { AddFunctionOpcode(cRSub); --mStackPtr; } else // (-x)-y -> -(x+y) { AddFunctionOpcode(cAdd); --mStackPtr; AddFunctionOpcode(cNeg); } } FP_FlushImmed(false); #undef FP_FlushImmed return function; } template inline const char* FunctionParserBase::CompileComparison(const char* function) { unsigned op=0; while(true) { function = CompileAddition(function); if(!function) return 0; if(op) { AddFunctionOpcode(op); --mStackPtr; } switch(*function) { case '=': ++function; op = cEqual; break; case '!': if(function[1] == '=') { function += 2; op = cNEqual; break; } // If '=' does not follow '!', a syntax error will // be generated at the outermost parsing level return function; case '<': if(function[1] == '=') { function += 2; op = cLessOrEq; break; } ++function; op = cLess; break; case '>': if(function[1] == '=') { function += 2; op = cGreaterOrEq; break; } ++function; op = cGreater; break; default: return function; } SkipSpace(function); } return function; } template inline const char* FunctionParserBase::CompileAnd(const char* function) { std::size_t param0end=0; while(true) { function = CompileComparison(function); if(!function) return 0; if(param0end) { if(mData->mByteCode.back() == cNotNot) mData->mByteCode.pop_back(); AddFunctionOpcode(cAnd); --mStackPtr; } if(*function != '&') break; ++function; SkipSpace(function); param0end = mData->mByteCode.size(); } return function; } template const char* FunctionParserBase::CompileExpression(const char* function) { std::size_t param0end=0; while(true) { SkipSpace(function); function = CompileAnd(function); if(!function) return 0; if(param0end) { if(mData->mByteCode.back() == cNotNot) mData->mByteCode.pop_back(); AddFunctionOpcode(cOr); --mStackPtr; } if(*function != '|') break; ++function; param0end = mData->mByteCode.size(); } return function; } template const char* FunctionParserBase::Compile(const char* function) { while(true) { // Check if an identifier appears as first token: SkipSpace(function); unsigned nameLength = readIdentifier(function); if(nameLength > 0 && !(nameLength & 0x80000000U)) { typename Data::InlineVariable inlineVar = { NamePtr(function, nameLength), 0 }; // Check if it's an unknown identifier: typename NamePtrsMap::iterator nameIter = mData->mNamePtrs.find(inlineVar.mName); if(nameIter == mData->mNamePtrs.end()) { const char* function2 = function + nameLength; SkipSpace(function2); // Check if ":=" follows the unknown identifier: if(function2[0] == ':' && function2[1] == '=') { // Parse the expression that follows and create the // inline variable: function2 = CompileExpression(function2 + 2); if(!function2) return 0; if(*function2 != ';') return function2; inlineVar.mFetchIndex = mStackPtr - 1; mData->mInlineVarNames.push_back(inlineVar); // Continue with the expression after the ';': function = function2 + 1; continue; } } } break; } return CompileExpression(function); } template template inline void FunctionParserBase::PushOpcodeParam (unsigned value) { mData->mByteCode.push_back(value | (PutFlag ? FP_ParamGuardMask : 0u)); if(PutFlag) mData->mHasByteCodeFlags = true; } template template inline void FunctionParserBase::PutOpcodeParamAt (unsigned value, unsigned offset) { mData->mByteCode[offset] = value | (PutFlag ? FP_ParamGuardMask : 0u); if(PutFlag) mData->mHasByteCodeFlags = true; } //=========================================================================== // Function evaluation //=========================================================================== template Value_t FunctionParserBase::Eval(const Value_t* Vars) { if(mData->mParseErrorType != FP_NO_ERROR) return Value_t(0); const unsigned* const byteCode = &(mData->mByteCode[0]); const Value_t* const immed = mData->mImmed.empty() ? 0 : &(mData->mImmed[0]); const unsigned byteCodeSize = unsigned(mData->mByteCode.size()); unsigned IP, DP=0; int SP=-1; #ifdef FP_USE_THREAD_SAFE_EVAL /* If Eval() may be called by multiple threads simultaneously, * then Eval() must allocate its own stack. */ #ifdef FP_USE_THREAD_SAFE_EVAL_WITH_ALLOCA /* alloca() allocates room from the hardware stack. * It is automatically freed when the function returns. */ Value_t* const Stack = (Value_t*)alloca(mData->mStackSize*sizeof(Value_t)); #else /* Allocate from the heap. Ensure that it is freed * automatically no matter which exit path is taken. */ struct AutoDealloc { Value_t* ptr; ~AutoDealloc() { delete[] ptr; } } AutoDeallocStack = { new Value_t[mData->mStackSize] }; Value_t*& Stack = AutoDeallocStack.ptr; #endif #else /* No thread safety, so use a global stack. */ std::vector& Stack = mData->mStack; #endif for(IP=0; IP::result == false && (Stack[SP] < Value_t(-1) || Stack[SP] > Value_t(1))) { mData->mEvalErrorType=4; return Value_t(0); } Stack[SP] = fp_acos(Stack[SP]); break; case cAcosh: if(IsComplexType::result == false && Stack[SP] < Value_t(1)) { mData->mEvalErrorType=4; return Value_t(0); } Stack[SP] = fp_acosh(Stack[SP]); break; case cAsin: if(IsComplexType::result == false && (Stack[SP] < Value_t(-1) || Stack[SP] > Value_t(1))) { mData->mEvalErrorType=4; return Value_t(0); } Stack[SP] = fp_asin(Stack[SP]); break; case cAsinh: Stack[SP] = fp_asinh(Stack[SP]); break; case cAtan: Stack[SP] = fp_atan(Stack[SP]); break; case cAtan2: Stack[SP-1] = fp_atan2(Stack[SP-1], Stack[SP]); --SP; break; case cAtanh: if(IsComplexType::result ? (Stack[SP] == Value_t(-1) || Stack[SP] == Value_t(1)) : (Stack[SP] <= Value_t(-1) || Stack[SP] >= Value_t(1))) { mData->mEvalErrorType=4; return Value_t(0); } Stack[SP] = fp_atanh(Stack[SP]); break; case cCbrt: Stack[SP] = fp_cbrt(Stack[SP]); break; case cCeil: Stack[SP] = fp_ceil(Stack[SP]); break; case cCos: Stack[SP] = fp_cos(Stack[SP]); break; case cCosh: Stack[SP] = fp_cosh(Stack[SP]); break; case cCot: { const Value_t t = fp_tan(Stack[SP]); if(t == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP] = Value_t(1)/t; break; } case cCsc: { const Value_t s = fp_sin(Stack[SP]); if(s == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP] = Value_t(1)/s; break; } case cExp: Stack[SP] = fp_exp(Stack[SP]); break; case cExp2: Stack[SP] = fp_exp2(Stack[SP]); break; case cFloor: Stack[SP] = fp_floor(Stack[SP]); break; case cHypot: Stack[SP-1] = fp_hypot(Stack[SP-1], Stack[SP]); --SP; break; case cIf: if(fp_truth(Stack[SP--])) IP += 2; else { const unsigned* buf = &byteCode[IP+1]; IP = buf[0]; DP = buf[1]; } break; case cInt: Stack[SP] = fp_int(Stack[SP]); break; case cLog: if(IsComplexType::result ? Stack[SP] == Value_t(0) : !(Stack[SP] > Value_t(0))) { mData->mEvalErrorType=3; return Value_t(0); } Stack[SP] = fp_log(Stack[SP]); break; case cLog10: if(IsComplexType::result ? Stack[SP] == Value_t(0) : !(Stack[SP] > Value_t(0))) { mData->mEvalErrorType=3; return Value_t(0); } Stack[SP] = fp_log10(Stack[SP]); break; case cLog2: if(IsComplexType::result ? Stack[SP] == Value_t(0) : !(Stack[SP] > Value_t(0))) { mData->mEvalErrorType=3; return Value_t(0); } Stack[SP] = fp_log2(Stack[SP]); break; case cMax: Stack[SP-1] = fp_max(Stack[SP-1], Stack[SP]); --SP; break; case cMin: Stack[SP-1] = fp_min(Stack[SP-1], Stack[SP]); --SP; break; case cPow: // x:Negative ^ y:NonInteger is failure, // except when the reciprocal of y forms an integer /*if(IsComplexType::result == false && Stack[SP-1] < Value_t(0) && !isInteger(Stack[SP]) && !isInteger(1.0 / Stack[SP])) { mEvalErrorType=3; return Value_t(0); }*/ // x:0 ^ y:negative is failure if(Stack[SP-1] == Value_t(0) && Stack[SP] < Value_t(0)) { mData->mEvalErrorType=3; return Value_t(0); } Stack[SP-1] = fp_pow(Stack[SP-1], Stack[SP]); --SP; break; case cTrunc: Stack[SP] = fp_trunc(Stack[SP]); break; case cSec: { const Value_t c = fp_cos(Stack[SP]); if(c == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP] = Value_t(1)/c; break; } case cSin: Stack[SP] = fp_sin(Stack[SP]); break; case cSinh: Stack[SP] = fp_sinh(Stack[SP]); break; case cSqrt: if(IsComplexType::result == false && Stack[SP] < Value_t(0)) { mData->mEvalErrorType=2; return Value_t(0); } Stack[SP] = fp_sqrt(Stack[SP]); break; case cTan: Stack[SP] = fp_tan(Stack[SP]); break; case cTanh: Stack[SP] = fp_tanh(Stack[SP]); break; // Misc: case cImmed: Stack[++SP] = immed[DP++]; break; case cJump: { const unsigned* buf = &byteCode[IP+1]; IP = buf[0]; DP = buf[1]; break; } // Operators: case cNeg: Stack[SP] = -Stack[SP]; break; case cAdd: Stack[SP-1] += Stack[SP]; --SP; break; case cSub: Stack[SP-1] -= Stack[SP]; --SP; break; case cMul: Stack[SP-1] *= Stack[SP]; --SP; break; case cDiv: if(Stack[SP] == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP-1] /= Stack[SP]; --SP; break; case cMod: if(Stack[SP] == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP-1] = fp_mod(Stack[SP-1], Stack[SP]); --SP; break; case cEqual: Stack[SP-1] = fp_equal(Stack[SP-1], Stack[SP]); --SP; break; case cNEqual: Stack[SP-1] = fp_nequal(Stack[SP-1], Stack[SP]); --SP; break; case cLess: Stack[SP-1] = fp_less(Stack[SP-1], Stack[SP]); --SP; break; case cLessOrEq: Stack[SP-1] = fp_lessOrEq(Stack[SP-1], Stack[SP]); --SP; break; case cGreater: Stack[SP-1] = fp_less(Stack[SP], Stack[SP-1]); --SP; break; case cGreaterOrEq: Stack[SP-1] = fp_lessOrEq(Stack[SP], Stack[SP-1]); --SP; break; case cNot: Stack[SP] = fp_not(Stack[SP]); break; case cNotNot: Stack[SP] = fp_notNot(Stack[SP]); break; case cAnd: Stack[SP-1] = fp_and(Stack[SP-1], Stack[SP]); --SP; break; case cOr: Stack[SP-1] = fp_or(Stack[SP-1], Stack[SP]); --SP; break; // Degrees-radians conversion: case cDeg: Stack[SP] = RadiansToDegrees(Stack[SP]); break; case cRad: Stack[SP] = DegreesToRadians(Stack[SP]); break; // User-defined function calls: case cFCall: { const unsigned index = byteCode[++IP]; const unsigned params = mData->mFuncPtrs[index].mParams; const Value_t retVal = mData->mFuncPtrs[index].mRawFuncPtr ? mData->mFuncPtrs[index].mRawFuncPtr(&Stack[SP-params+1]) : mData->mFuncPtrs[index].mFuncWrapperPtr->callFunction (&Stack[SP-params+1]); SP -= int(params)-1; Stack[SP] = retVal; break; } case cPCall: { unsigned index = byteCode[++IP]; unsigned params = mData->mFuncParsers[index].mParams; Value_t retVal = mData->mFuncParsers[index].mParserPtr->Eval (&Stack[SP-params+1]); SP -= int(params)-1; Stack[SP] = retVal; const int error = mData->mFuncParsers[index].mParserPtr->EvalError(); if(error) { mData->mEvalErrorType = error; return 0; } break; } case cFetch: { unsigned stackOffs = byteCode[++IP]; Stack[SP+1] = Stack[stackOffs]; ++SP; break; } #ifdef FP_SUPPORT_OPTIMIZER case cPopNMov: { unsigned stackOffs_target = byteCode[++IP]; unsigned stackOffs_source = byteCode[++IP]; Stack[stackOffs_target] = Stack[stackOffs_source]; SP = stackOffs_target; break; } case cLog2by: if(IsComplexType::result ? Stack[SP-1] == Value_t(0) : !(Stack[SP-1] > Value_t(0))) { mData->mEvalErrorType=3; return Value_t(0); } Stack[SP-1] = fp_log2(Stack[SP-1]) * Stack[SP]; --SP; break; case cNop: break; #endif // FP_SUPPORT_OPTIMIZER case cSinCos: fp_sinCos(Stack[SP], Stack[SP+1], Stack[SP]); ++SP; break; case cSinhCosh: fp_sinhCosh(Stack[SP], Stack[SP+1], Stack[SP]); ++SP; break; case cAbsNot: Stack[SP] = fp_absNot(Stack[SP]); break; case cAbsNotNot: Stack[SP] = fp_absNotNot(Stack[SP]); break; case cAbsAnd: Stack[SP-1] = fp_absAnd(Stack[SP-1], Stack[SP]); --SP; break; case cAbsOr: Stack[SP-1] = fp_absOr(Stack[SP-1], Stack[SP]); --SP; break; case cAbsIf: if(fp_absTruth(Stack[SP--])) IP += 2; else { const unsigned* buf = &byteCode[IP+1]; IP = buf[0]; DP = buf[1]; } break; case cDup: Stack[SP+1] = Stack[SP]; ++SP; break; case cInv: if(Stack[SP] == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP] = Value_t(1)/Stack[SP]; break; case cSqr: Stack[SP] = Stack[SP]*Stack[SP]; break; case cRDiv: if(Stack[SP-1] == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP-1] = Stack[SP] / Stack[SP-1]; --SP; break; case cRSub: Stack[SP-1] = Stack[SP] - Stack[SP-1]; --SP; break; case cRSqrt: if(Stack[SP] == Value_t(0)) { mData->mEvalErrorType=1; return Value_t(0); } Stack[SP] = Value_t(1) / fp_sqrt(Stack[SP]); break; #ifdef FP_SUPPORT_COMPLEX_NUMBERS case cReal: Stack[SP] = fp_real(Stack[SP]); break; case cImag: Stack[SP] = fp_imag(Stack[SP]); break; case cArg: Stack[SP] = fp_arg(Stack[SP]); break; case cConj: Stack[SP] = fp_conj(Stack[SP]); break; case cPolar: Stack[SP-1] = fp_polar(Stack[SP-1], Stack[SP]); --SP; break; #endif // Variables: default: Stack[++SP] = Vars[byteCode[IP]-VarBegin]; } } mData->mEvalErrorType=0; return Stack[SP]; } //=========================================================================== // Variable deduction //=========================================================================== namespace { template int deduceVariables(FunctionParserBase& fParser, const char* funcStr, std::string& destVarString, int* amountOfVariablesFound, std::vector* destVarNames, bool useDegrees) { typedef std::set StrSet; StrSet varNames; int oldIndex = -1; while(true) { destVarString.clear(); for(StrSet::iterator iter = varNames.begin(); iter != varNames.end(); ++iter) { if(iter != varNames.begin()) destVarString += ","; destVarString += *iter; } const int index = fParser.Parse(funcStr, destVarString, useDegrees); if(index < 0) break; if(index == oldIndex) return index; unsigned nameLength = readIdentifier(funcStr + index); if(nameLength & 0x80000000U) return index; if(nameLength == 0) return index; varNames.insert(std::string(funcStr + index, nameLength)); oldIndex = index; } if(amountOfVariablesFound) *amountOfVariablesFound = int(varNames.size()); if(destVarNames) destVarNames->assign(varNames.begin(), varNames.end()); return -1; } } template int FunctionParserBase::ParseAndDeduceVariables (const std::string& function, int* amountOfVariablesFound, bool useDegrees) { std::string varString; return deduceVariables(*this, function.c_str(), varString, amountOfVariablesFound, 0, useDegrees); } template int FunctionParserBase::ParseAndDeduceVariables (const std::string& function, std::string& resultVarString, int* amountOfVariablesFound, bool useDegrees) { std::string varString; const int index = deduceVariables(*this, function.c_str(), varString, amountOfVariablesFound, 0, useDegrees); if(index < 0) resultVarString = varString; return index; } template int FunctionParserBase::ParseAndDeduceVariables (const std::string& function, std::vector& resultVars, bool useDegrees) { std::string varString; std::vector vars; const int index = deduceVariables(*this, function.c_str(), varString, 0, &vars, useDegrees); if(index < 0) resultVars.swap(vars); return index; } #ifdef FUNCTIONPARSER_SUPPORT_DEBUGGING //=========================================================================== // Bytecode injection //=========================================================================== template void FunctionParserBase::InjectRawByteCode (const unsigned* bytecode, unsigned bytecodeAmount, const Value_t* immed, unsigned immedAmount, unsigned stackSize) { CopyOnWrite(); mData->mByteCode.assign(bytecode, bytecode + bytecodeAmount); mData->mImmed.assign(immed, immed + immedAmount); mData->mStackSize = stackSize; #ifndef FP_USE_THREAD_SAFE_EVAL mData->mStack.resize(stackSize); #endif } //=========================================================================== // Debug output //=========================================================================== #include #include namespace { inline void printHex(std::ostream& dest, unsigned n) { std::ios::fmtflags flags = dest.flags(); dest.width(4); dest.fill('0'); std::hex(dest); //uppercase(dest); dest << n; dest.flags(flags); } void padLine(std::ostringstream& dest, unsigned destLength) { for(std::size_t currentLength = dest.str().length(); currentLength < destLength; ++currentLength) { dest << ' '; } } const struct PowiMuliType { unsigned opcode_square; unsigned opcode_cumulate; unsigned opcode_invert; unsigned opcode_half; unsigned opcode_invhalf; } iseq_powi = {cSqr,cMul,cInv,cSqrt,cRSqrt}, iseq_muli = {~unsigned(0), cAdd,cNeg, ~unsigned(0),~unsigned(0) }; template Value_t ParsePowiMuli( const PowiMuliType& opcodes, const std::vector& ByteCode, unsigned& IP, unsigned limit, std::size_t factor_stack_base, std::vector& stack, bool IgnoreExcess) { Value_t result = Value_t(1); while(IP < limit) { if(ByteCode[IP] == opcodes.opcode_square) { if(!isInteger(result)) break; result *= Value_t(2); ++IP; continue; } if(ByteCode[IP] == opcodes.opcode_invert) { if(result < Value_t(0)) break; result = -result; ++IP; continue; } if(ByteCode[IP] == opcodes.opcode_half) { if(result > Value_t(0) && isEvenInteger(result)) break; if(isInteger(result * Value_t(0.5))) break; result *= Value_t(0.5); ++IP; continue; } if(ByteCode[IP] == opcodes.opcode_invhalf) { if(result > Value_t(0) && isEvenInteger(result)) break; if(isInteger(result * Value_t(-0.5))) break; result *= Value_t(-0.5); ++IP; continue; } unsigned dup_fetch_pos = IP; Value_t lhs = Value_t(1); if(ByteCode[IP] == cFetch) { unsigned index = ByteCode[++IP]; if(index < factor_stack_base || std::size_t(index-factor_stack_base) >= stack.size()) { // It wasn't a powi-fetch after all IP = dup_fetch_pos; break; } lhs = stack[index - factor_stack_base]; // Note: ^This assumes that cFetch of recentmost // is always converted into cDup. goto dup_or_fetch; } if(ByteCode[IP] == cDup) { lhs = result; goto dup_or_fetch; dup_or_fetch: stack.push_back(result); ++IP; Value_t subexponent = ParsePowiMuli (opcodes, ByteCode, IP, limit, factor_stack_base, stack, IgnoreExcess); if(IP >= limit && IgnoreExcess) return lhs*subexponent; if(IP >= limit || ByteCode[IP] != opcodes.opcode_cumulate) { // It wasn't a powi-dup after all IP = dup_fetch_pos; break; } ++IP; // skip opcode_cumulate stack.pop_back(); result += lhs*subexponent; continue; } break; } return result; } template Value_t ParsePowiSequence(const std::vector& ByteCode, unsigned& IP, unsigned limit, std::size_t factor_stack_base, bool IgnoreExcess = false) { std::vector stack; stack.push_back(Value_t(1)); return ParsePowiMuli(iseq_powi, ByteCode, IP, limit, factor_stack_base, stack, IgnoreExcess); } template Value_t ParseMuliSequence(const std::vector& ByteCode, unsigned& IP, unsigned limit, std::size_t factor_stack_base, bool IgnoreExcess = false) { std::vector stack; stack.push_back(Value_t(1)); return ParsePowiMuli(iseq_muli, ByteCode, IP, limit, factor_stack_base, stack, IgnoreExcess); } struct IfInfo { std::pair condition; std::pair thenbranch; unsigned endif_location; IfInfo() : condition(), thenbranch(), endif_location() { } }; } template void FunctionParserBase::PrintByteCode(std::ostream& dest, bool showExpression) const { dest << "Size of stack: " << mData->mStackSize << "\n"; std::ostringstream outputBuffer; std::ostream& output = (showExpression ? outputBuffer : dest); const std::vector& ByteCode = mData->mByteCode; const std::vector& Immed = mData->mImmed; std::vector > stack; std::vector if_stack; for(unsigned IP = 0, DP = 0; IP <= ByteCode.size(); ++IP) { after_powi_or_muli:; std::string n; bool out_params = false; unsigned params = 2, produces = 1, opcode = 0; if(showExpression && !if_stack.empty() && ( // Normal If termination rule: if_stack.back().endif_location == IP // This rule matches when cJumps are threaded: || (IP < ByteCode.size() && ByteCode[IP] == cJump && !if_stack.back().thenbranch.second.empty()) )) { printHex(output, IP); if(if_stack.back().endif_location == IP) output << ": ----- (phi)"; else output << ": ----- (phi+)"; stack.resize(stack.size()+2); std::swap(stack[stack.size()-3], stack[stack.size()-1]); std::swap(if_stack.back().condition, stack[stack.size()-3]); std::swap(if_stack.back().thenbranch, stack[stack.size()-2]); opcode = cIf; params = 3; --IP; if_stack.pop_back(); } else { if(IP >= ByteCode.size()) break; opcode = ByteCode[IP]; if(showExpression && ( opcode == cSqr || opcode == cDup || opcode == cInv || opcode == cSqrt || opcode == cRSqrt || opcode == cFetch )) { unsigned changed_ip = IP; Value_t exponent = ParsePowiSequence (ByteCode, changed_ip, if_stack.empty() ? (unsigned)ByteCode.size() : if_stack.back().endif_location, stack.size()-1); std::string operation_prefix; std::ostringstream operation_value; int prio = 0; if(exponent == Value_t(1.0)) { if(opcode != cDup) goto not_powi_or_muli; Value_t factor = ParseMuliSequence (ByteCode, changed_ip, if_stack.empty() ? (unsigned)ByteCode.size() : if_stack.back().endif_location, stack.size()-1); if(factor == Value_t(1) || factor == Value_t(-1)) goto not_powi_or_muli; operation_prefix = "*"; operation_value << factor; prio = 3; } else { prio = 2; operation_prefix = "^"; operation_value << exponent; } //unsigned explanation_before = changed_ip-2; unsigned explanation_before = changed_ip-1; const char* explanation_prefix = "_"; for(const unsigned first_ip = IP; IP < changed_ip; ++IP) { printHex(output, IP); output << ": "; const char* sep = "|"; if(first_ip+1 == changed_ip) { sep = "="; explanation_prefix = " "; } else if(IP == first_ip) sep = "\\"; else if(IP+1 == changed_ip) sep = "/"; else explanation_prefix = "="; switch(ByteCode[IP]) { case cInv: output << "inv"; break; case cNeg: output << "neg"; break; case cDup: output << "dup"; break; case cSqr: output << "sqr"; break; case cMul: output << "mul"; break; case cAdd: output << "add"; break; case cCbrt: output << "cbrt"; break; case cSqrt: output << "sqrt"; break; case cRSqrt: output << "rsqrt"; break; case cFetch: { unsigned index = ByteCode[++IP]; output << "cFetch(" << index << ")"; break; } default: break; } padLine(outputBuffer, 20); output << sep; if(IP >= explanation_before) { explanation_before = (unsigned)ByteCode.size(); output << explanation_prefix << '[' << (stack.size()-1) << ']'; std::string last = stack.back().second; if(stack.back().first >= prio) last = "(" + last + ")"; output << last; output << operation_prefix; output << operation_value.str(); } else { unsigned p = first_ip; Value_t exp = operation_prefix=="^" ? ParsePowiSequence (ByteCode, p, IP+1, stack.size()-1, true) : ParseMuliSequence (ByteCode, p, IP+1, stack.size()-1, true); std::string last = stack.back().second; if(stack.back().first >= prio) last = "(" + last + ")"; output << " ..." << last; output << operation_prefix; output << exp; } dest << outputBuffer.str() << std::endl; outputBuffer.str(""); } std::string& last = stack.back().second; if(stack.back().first >= prio) last = "(" + last + ")"; last += operation_prefix; last += operation_value.str(); stack.back().first = prio; goto after_powi_or_muli; } not_powi_or_muli:; printHex(output, IP); output << ": "; switch(opcode) { case cIf: { unsigned label = ByteCode[IP+1]+1; output << "jz "; printHex(output, label); params = 1; produces = 0; IP += 2; if_stack.resize(if_stack.size() + 1); std::swap( if_stack.back().condition, stack.back() ); if_stack.back().endif_location = (unsigned) ByteCode.size(); stack.pop_back(); break; } case cAbsIf: { unsigned dp = ByteCode[IP+2]; unsigned label = ByteCode[IP+1]+1; output << "jz_abs " << dp << ","; printHex(output, label); params = 1; produces = 0; IP += 2; if_stack.resize(if_stack.size() + 1); std::swap( if_stack.back().condition, stack.back() ); if_stack.back().endif_location = (unsigned) ByteCode.size(); stack.pop_back(); break; } case cJump: { unsigned dp = ByteCode[IP+2]; unsigned label = ByteCode[IP+1]+1; if(!if_stack.empty() && !stack.empty()) { std::swap(if_stack.back().thenbranch, stack.back()); if_stack.back().endif_location = label; stack.pop_back(); } output << "jump " << dp << ","; printHex(output, label); params = 0; produces = 0; IP += 2; break; } case cImmed: { if(showExpression) { std::ostringstream buf; buf.precision(8); buf << Immed[DP]; stack.push_back( std::make_pair(0, buf.str()) ); } output.precision(8); output << "push " << Immed[DP]; ++DP; produces = 0; break; } case cFCall: { const unsigned index = ByteCode[++IP]; params = mData->mFuncPtrs[index].mParams; static std::string name; name = "f:" + findName(mData->mNamePtrs, index, NameData::FUNC_PTR); n = name.c_str(); out_params = true; break; } case cPCall: { const unsigned index = ByteCode[++IP]; params = mData->mFuncParsers[index].mParams; static std::string name; name = "p:" + findName(mData->mNamePtrs, index, NameData::PARSER_PTR); n = name.c_str(); out_params = true; break; } default: if(IsVarOpcode(opcode)) { if(showExpression) { stack.push_back(std::make_pair(0, (findName(mData->mNamePtrs, opcode, NameData::VARIABLE)))); } output << "push Var" << opcode-VarBegin; produces = 0; } else { switch(OPCODE(opcode)) { case cNeg: n = "neg"; params = 1; break; case cAdd: n = "add"; break; case cSub: n = "sub"; break; case cMul: n = "mul"; break; case cDiv: n = "div"; break; case cMod: n = "mod"; break; case cPow: n = "pow"; break; case cEqual: n = "eq"; break; case cNEqual: n = "neq"; break; case cLess: n = "lt"; break; case cLessOrEq: n = "le"; break; case cGreater: n = "gt"; break; case cGreaterOrEq: n = "ge"; break; case cAnd: n = "and"; break; case cOr: n = "or"; break; case cNot: n = "not"; params = 1; break; case cNotNot: n = "notnot"; params = 1; break; case cDeg: n = "deg"; params = 1; break; case cRad: n = "rad"; params = 1; break; case cFetch: { unsigned index = ByteCode[++IP]; if(showExpression && index < stack.size()) stack.push_back(stack[index]); output << "cFetch(" << index << ")"; produces = 0; break; } #ifdef FP_SUPPORT_OPTIMIZER case cLog2by: n = "log2by"; params = 2; out_params = 1; break; case cPopNMov: { std::size_t a = ByteCode[++IP]; std::size_t b = ByteCode[++IP]; if(showExpression && b < stack.size()) { std::pair stacktop(0, "?"); if(b < stack.size()) stacktop = stack[b]; stack.resize(a); stack.push_back(stacktop); } output << "cPopNMov(" << a << ", " << b << ")"; produces = 0; break; } case cNop: output << "nop"; params = 0; produces = 0; break; #endif case cSinCos: { if(showExpression) { std::pair sin = stack.back(); std::pair cos( 0, "cos(" + sin.second + ")"); sin.first = 0; sin.second = "sin(" + sin.second + ")"; stack.back() = sin; stack.push_back(cos); } output << "sincos"; produces = 0; break; } case cSinhCosh: { if(showExpression) { std::pair sinh = stack.back(); std::pair cosh( 0, "cosh(" + sinh.second + ")"); sinh.first = 0; sinh.second = "sinh(" + sinh.second + ")"; stack.back() = sinh; stack.push_back(cosh); } output << "sinhcosh"; produces = 0; break; } case cAbsAnd: n = "abs_and"; break; case cAbsOr: n = "abs_or"; break; case cAbsNot: n = "abs_not"; params = 1; break; case cAbsNotNot: n = "abs_notnot"; params = 1; break; case cDup: { if(showExpression) stack.push_back(stack.back()); output << "dup"; produces = 0; break; } case cInv: n = "inv"; params = 1; break; case cSqr: n = "sqr"; params = 1; break; case cRDiv: n = "rdiv"; break; case cRSub: n = "rsub"; break; case cRSqrt: n = "rsqrt"; params = 1; break; default: n = Functions[opcode-cAbs].name; params = Functions[opcode-cAbs].params; out_params = params != 1; } } } } if(produces) output << n; if(out_params) output << " (" << params << ")"; if(showExpression) { padLine(outputBuffer, 20); if(produces > 0) { std::ostringstream buf; const char *paramsep = ",", *suff = ""; int prio = 0; bool commutative = false; switch(opcode) { case cIf: buf << "if("; suff = ")"; break; case cAbsIf: buf << "if("; suff = ")"; break; case cOr: prio = 6; paramsep = "|"; commutative = true; break; case cAnd: prio = 5; paramsep = "&"; commutative = true; break; case cAdd: prio = 4; paramsep = "+"; commutative = true; break; case cSub: prio = 4; paramsep = "-"; break; case cMul: prio = 3; paramsep = "*"; commutative = true; break; case cDiv: prio = 3; paramsep = "/"; break; case cPow: prio = 2; paramsep = "^"; break; case cAbsOr: prio = 6; paramsep = "|"; commutative = true; break; case cAbsAnd: prio = 5; paramsep = "&"; commutative = true; break; case cSqr: prio = 2; suff = "^2"; break; case cNeg: buf << "(-("; suff = "))"; break; case cNot: buf << "(!("; suff = "))"; break; default: buf << n << '('; suff = ")"; } const char* sep = ""; for(unsigned a=0; a& prev = stack[stack.size() - params + a]; if(prio > 0 && (prev.first > prio || (prev.first==prio && !commutative))) buf << '(' << prev.second << ')'; else buf << prev.second; } sep = paramsep; } if(stack.size() >= params) stack.resize(stack.size() - params); else stack.clear(); buf << suff; stack.push_back(std::make_pair(prio, buf.str())); //if(n.size() <= 4 && !out_params) padLine(outputBuffer, 20); } //padLine(outputBuffer, 20); output << "= "; if(((opcode == cIf || opcode == cAbsIf) && params != 3) || opcode == cJump #ifdef FP_SUPPORT_OPTIMIZER || opcode == cNop #endif ) output << "(void)"; else if(stack.empty()) output << "[?] ?"; else output << '[' << (stack.size()-1) << ']' << stack.back().second; } if(showExpression) { dest << outputBuffer.str() << std::endl; outputBuffer.str(""); } else output << std::endl; } dest << std::flush; } #endif #ifndef FP_SUPPORT_OPTIMIZER template void FunctionParserBase::Optimize() { // Do nothing if no optimizations are supported. } #endif #define FUNCTIONPARSER_INSTANTIATE_CLASS(type) \ template class FunctionParserBase< type >; #ifndef FP_DISABLE_DOUBLE_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(double) #endif #ifdef FP_SUPPORT_FLOAT_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(float) #endif #ifdef FP_SUPPORT_LONG_DOUBLE_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(long double) #endif #ifdef FP_SUPPORT_LONG_INT_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(long) #endif #ifdef FP_SUPPORT_MPFR_FLOAT_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(MpfrFloat) #endif #ifdef FP_SUPPORT_GMP_INT_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(GmpInt) #endif #ifdef FP_SUPPORT_COMPLEX_DOUBLE_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(std::complex) #endif #ifdef FP_SUPPORT_COMPLEX_FLOAT_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(std::complex) #endif #ifdef FP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE FUNCTIONPARSER_INSTANTIATE_CLASS(std::complex) #endif fparserc++-4.5.2/fparser.hh000066400000000000000000000170671332731714600154620ustar00rootroot00000000000000/***************************************************************************\ |* Function Parser for C++ v4.5.2 *| |*-------------------------------------------------------------------------*| |* Copyright: Juha Nieminen, Joel Yliluoma *| |* *| |* This library is distributed under the terms of the *| |* GNU Lesser General Public License version 3. *| |* (See lgpl.txt and gpl.txt for the license text.) *| \***************************************************************************/ #ifndef ONCE_FPARSER_H_ #define ONCE_FPARSER_H_ #include #include #ifdef FUNCTIONPARSER_SUPPORT_DEBUGGING #include #endif #ifdef _MSC_VER // Visual Studio's warning about missing definitions for the explicit // FunctionParserBase instantiations is irrelevant here. #pragma warning(disable : 4661) #endif namespace FPoptimizer_CodeTree { template class CodeTree; } template class FunctionParserBase { public: enum ParseErrorType { SYNTAX_ERROR=0, MISM_PARENTH, MISSING_PARENTH, EMPTY_PARENTH, EXPECT_OPERATOR, OUT_OF_MEMORY, UNEXPECTED_ERROR, INVALID_VARS, ILL_PARAMS_AMOUNT, PREMATURE_EOS, EXPECT_PARENTH_FUNC, UNKNOWN_IDENTIFIER, NO_FUNCTION_PARSED_YET, FP_NO_ERROR }; typedef Value_t value_type; int Parse(const char* Function, const std::string& Vars, bool useDegrees = false); int Parse(const std::string& Function, const std::string& Vars, bool useDegrees = false); void setDelimiterChar(char); static Value_t epsilon(); static void setEpsilon(Value_t); const char* ErrorMsg() const; ParseErrorType GetParseErrorType() const; Value_t Eval(const Value_t* Vars); int EvalError() const; bool AddConstant(const std::string& name, Value_t value); bool AddUnit(const std::string& name, Value_t value); typedef Value_t (*FunctionPtr)(const Value_t*); bool AddFunction(const std::string& name, FunctionPtr, unsigned paramsAmount); bool AddFunction(const std::string& name, FunctionParserBase&); class FunctionWrapper; template bool AddFunctionWrapper(const std::string& name, const DerivedWrapper&, unsigned paramsAmount); FunctionWrapper* GetFunctionWrapper(const std::string& name); bool RemoveIdentifier(const std::string& name); void Optimize(); int ParseAndDeduceVariables(const std::string& function, int* amountOfVariablesFound = 0, bool useDegrees = false); int ParseAndDeduceVariables(const std::string& function, std::string& resultVarString, int* amountOfVariablesFound = 0, bool useDegrees = false); int ParseAndDeduceVariables(const std::string& function, std::vector& resultVars, bool useDegrees = false); FunctionParserBase(); ~FunctionParserBase(); // Copy constructor and assignment operator (implemented using the // copy-on-write technique for efficiency): FunctionParserBase(const FunctionParserBase&); FunctionParserBase& operator=(const FunctionParserBase&); void ForceDeepCopy(); #ifdef FUNCTIONPARSER_SUPPORT_DEBUGGING // For debugging purposes only. // Performs no sanity checks or anything. If the values are wrong, the // library will crash. Do not use unless you know what you are doing. void InjectRawByteCode(const unsigned* bytecode, unsigned bytecodeAmount, const Value_t* immed, unsigned immedAmount, unsigned stackSize); void PrintByteCode(std::ostream& dest, bool showExpression = true) const; #endif //======================================================================== protected: //======================================================================== // A derived class can implement its own evaluation logic by using // the parser data (found in fptypes.hh). struct Data; Data* getParserData(); //======================================================================== private: //======================================================================== friend class FPoptimizer_CodeTree::CodeTree; // Private data: // ------------ Data* mData; unsigned mStackPtr; // Private methods: // --------------- void CopyOnWrite(); bool CheckRecursiveLinking(const FunctionParserBase*) const; bool NameExists(const char*, unsigned); bool ParseVariables(const std::string&); int ParseFunction(const char*, bool); const char* SetErrorType(ParseErrorType, const char*); void AddFunctionOpcode(unsigned); void AddImmedOpcode(Value_t v); void incStackPtr(); void CompilePowi(long); bool TryCompilePowi(Value_t); const char* CompileIf(const char*); const char* CompileFunctionParams(const char*, unsigned); const char* CompileElement(const char*); const char* CompilePossibleUnit(const char*); const char* CompilePow(const char*); const char* CompileUnaryMinus(const char*); const char* CompileMult(const char*); const char* CompileAddition(const char*); const char* CompileComparison(const char*); const char* CompileAnd(const char*); const char* CompileExpression(const char*); inline const char* CompileFunction(const char*, unsigned); inline const char* CompileParenthesis(const char*); inline const char* CompileLiteral(const char*); template inline void PushOpcodeParam(unsigned); template inline void PutOpcodeParamAt(unsigned, unsigned offset); const char* Compile(const char*); bool addFunctionWrapperPtr(const std::string&, FunctionWrapper*, unsigned); static void incFuncWrapperRefCount(FunctionWrapper*); static unsigned decFuncWrapperRefCount(FunctionWrapper*); protected: // Parsing utility functions static std::pair ParseLiteral(const char*); static unsigned ParseIdentifier(const char*); }; class FunctionParser: public FunctionParserBase {}; class FunctionParser_f: public FunctionParserBase {}; class FunctionParser_ld: public FunctionParserBase {}; class FunctionParser_li: public FunctionParserBase {}; #include class FunctionParser_cd: public FunctionParserBase > {}; class FunctionParser_cf: public FunctionParserBase > {}; class FunctionParser_cld: public FunctionParserBase > {}; template class FunctionParserBase::FunctionWrapper { unsigned mReferenceCount; friend class FunctionParserBase; public: FunctionWrapper(): mReferenceCount(1) {} FunctionWrapper(const FunctionWrapper&): mReferenceCount(1) {} virtual ~FunctionWrapper() {} FunctionWrapper& operator=(const FunctionWrapper&) { return *this; } virtual Value_t callFunction(const Value_t*) = 0; }; template template bool FunctionParserBase::AddFunctionWrapper (const std::string& name, const DerivedWrapper& wrapper, unsigned paramsAmount) { return addFunctionWrapperPtr (name, new DerivedWrapper(wrapper), paramsAmount); } #endif fparserc++-4.5.2/fparser_gmpint.hh000066400000000000000000000011021332731714600170170ustar00rootroot00000000000000/***************************************************************************\ |* Function Parser for C++ v4.5.2 *| |*-------------------------------------------------------------------------*| |* Copyright: Juha Nieminen *| \***************************************************************************/ #ifndef ONCE_FPARSER_GMPINT_H_ #define ONCE_FPARSER_GMPINT_H_ #include "fparser.hh" #include "mpfr/GmpInt.hh" class FunctionParser_gmpint: public FunctionParserBase {}; #endif fparserc++-4.5.2/fparser_mpfr.hh000066400000000000000000000011021332731714600164650ustar00rootroot00000000000000/***************************************************************************\ |* Function Parser for C++ v4.5.2 *| |*-------------------------------------------------------------------------*| |* Copyright: Juha Nieminen *| \***************************************************************************/ #ifndef ONCE_FPARSER_MPFR_H_ #define ONCE_FPARSER_MPFR_H_ #include "fparser.hh" #include "mpfr/MpfrFloat.hh" class FunctionParser_mpfr: public FunctionParserBase {}; #endif fparserc++-4.5.2/fpconfig.hh000066400000000000000000000066551332731714600156140ustar00rootroot00000000000000/***************************************************************************\ |* Function Parser for C++ v4.5.2 *| |*-------------------------------------------------------------------------*| |* Copyright: Juha Nieminen *| |* *| |* This library is distributed under the terms of the *| |* GNU Lesser General Public License version 3. *| |* (See lgpl.txt and gpl.txt for the license text.) *| \***************************************************************************/ // Configuration file // ------------------ /* NOTE: This file is for the internal use of the function parser only. You don't need to include this file in your source files, just include "fparser.hh". */ /* Uncomment any of these lines or define them in your compiler settings to enable the correspondent version of the parser. (These are disabled by default because they rely on C99 functions, and non-standard libraries in the case pf MPFR and GMP, and they make compiling needlessly slower and the resulting binary needlessly larger if they are not used in the program.) */ //#define FP_SUPPORT_FLOAT_TYPE //#define FP_SUPPORT_LONG_DOUBLE_TYPE //#define FP_SUPPORT_LONG_INT_TYPE //#define FP_SUPPORT_MPFR_FLOAT_TYPE //#define FP_SUPPORT_GMP_INT_TYPE //#define FP_SUPPORT_COMPLEX_DOUBLE_TYPE //#define FP_SUPPORT_COMPLEX_FLOAT_TYPE //#define FP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE /* If you are using FunctionParser_ld or FunctionParser_cld and your compiler supports the strtold() function, you should uncomment the following line. */ //#define FP_USE_STRTOLD /* Uncomment this line or define it in your compiler settings if you want to disable compiling the basic double version of the library, in case one of the above types is used but not the double type. (If the double type is not used, then disabling it makes compiling faster and the resulting binary smaller.) */ //#define FP_DISABLE_DOUBLE_TYPE /* Uncomment this line or define it in your compiler settings to make the parser use C++11 math functions. (Note that these may not be supported by all compilers.) */ //#define FP_SUPPORT_CPLUSPLUS11_MATH_FUNCS /* Whether to use shortcut evaluation for the & and | operators: */ #ifndef FP_DISABLE_SHORTCUT_LOGICAL_EVALUATION #define FP_ENABLE_SHORTCUT_LOGICAL_EVALUATION #endif /* Comment out the following lines out if you are not going to use the optimizer and want a slightly smaller library. The Optimize() method can still be called, but it will not do anything. If you are unsure, just leave it. It won't slow down the other parts of the library. */ #ifndef FP_NO_SUPPORT_OPTIMIZER #define FP_SUPPORT_OPTIMIZER #endif #if defined(FP_SUPPORT_COMPLEX_DOUBLE_TYPE) || defined(FP_SUPPORT_COMPLEX_FLOAT_TYPE) || defined(FP_SUPPORT_COMPLEX_LONG_DOUBLE_TYPE) #define FP_SUPPORT_COMPLEX_NUMBERS #endif /* No member function of FunctionParser is thread-safe. Most prominently, Eval() is not thread-safe. By uncommenting one of these lines the Eval() function can be made thread-safe at the cost of a possible small overhead. The second version requires that the compiler supports the alloca() function, which is not standard, but is faster. */ //#define FP_USE_THREAD_SAFE_EVAL //#define FP_USE_THREAD_SAFE_EVAL_WITH_ALLOCA fparserc++-4.5.2/fpoptimizer/000077500000000000000000000000001332731714600160345ustar00rootroot00000000000000fparserc++-4.5.2/fpoptimizer/bytecodesynth.cc000066400000000000000000000511261332731714600212340ustar00rootroot00000000000000#include "bytecodesynth.hh" #ifdef FP_SUPPORT_OPTIMIZER #include "opcodename.hh" #include "codetree.hh" using namespace FUNCTIONPARSERTYPES; namespace FPoptimizer_ByteCode { template struct SequenceOpCode { Value_t basevalue; unsigned op_flip; unsigned op_normal, op_normal_flip; unsigned op_inverse, op_inverse_flip; }; template const SequenceOpCode SequenceOpcodes::AddSequence = { Value_t(0), cNeg, cAdd, cAdd, cSub, cRSub }; template const SequenceOpCode SequenceOpcodes::MulSequence = { Value_t(1), cInv, cMul, cMul, cDiv, cRDiv }; /*******/ #define findName(a,b,c) "var" #define TryCompilePowi(o) false #define mData this #define mByteCode ByteCode #define mImmed Immed template void ByteCodeSynth::AddFunctionOpcode(unsigned opcode, Specializer) { int mStackPtr=0; # define FP_FLOAT_VERSION 1 # define FP_COMPLEX_VERSION 0 # include "extrasrc/fp_opcode_add.inc" # undef FP_COMPLEX_VERSION # undef FP_FLOAT_VERSION } template void ByteCodeSynth::AddFunctionOpcode(unsigned opcode, Specializer) { int mStackPtr=0; # define FP_FLOAT_VERSION 0 # define FP_COMPLEX_VERSION 0 # include "extrasrc/fp_opcode_add.inc" # undef FP_COMPLEX_VERSION # undef FP_FLOAT_VERSION } #ifdef FP_SUPPORT_COMPLEX_NUMBERS template void ByteCodeSynth::AddFunctionOpcode(unsigned opcode, Specializer) { int mStackPtr=0; # define FP_FLOAT_VERSION 1 # define FP_COMPLEX_VERSION 1 # include "extrasrc/fp_opcode_add.inc" # undef FP_COMPLEX_VERSION # undef FP_FLOAT_VERSION } template void ByteCodeSynth::AddFunctionOpcode(unsigned opcode, Specializer) { int mStackPtr=0; # define FP_FLOAT_VERSION 0 # define FP_COMPLEX_VERSION 1 # include "extrasrc/fp_opcode_add.inc" # undef FP_COMPLEX_VERSION # undef FP_FLOAT_VERSION } #endif #undef findName #undef mImmed #undef mByteCode #undef mData #undef TryCompilePowi /*******/ } using namespace FPoptimizer_ByteCode; #define POWI_TABLE_SIZE 256 #define POWI_WINDOW_SIZE 3 namespace FPoptimizer_ByteCode { #ifndef FP_GENERATING_POWI_TABLE extern const unsigned char powi_table[POWI_TABLE_SIZE]; const #endif unsigned char powi_table[POWI_TABLE_SIZE] = { 0, 1, 1, 1, 2, 1, 2, 1, /* 0 - 7 */ 4, 1, 2, 1, 4, 1, 2, 131, /* 8 - 15 */ 8, 1, 2, 1, 4, 1, 2, 1, /* 16 - 23 */ 8, 133, 2, 131, 4, 1, 15, 1, /* 24 - 31 */ 16, 1, 2, 1, 4, 1, 2, 131, /* 32 - 39 */ 8, 1, 2, 1, 4, 133, 2, 1, /* 40 - 47 */ 16, 1, 25, 131, 4, 1, 27, 5, /* 48 - 55 */ 8, 3, 2, 1, 30, 1, 31, 3, /* 56 - 63 */ 32, 1, 2, 1, 4, 1, 2, 1, /* 64 - 71 */ 8, 1, 2, 131, 4, 1, 39, 1, /* 72 - 79 */ 16, 137, 2, 1, 4, 133, 2, 131, /* 80 - 87 */ 8, 1, 45, 135, 4, 31, 2, 5, /* 88 - 95 */ 32, 1, 2, 131, 50, 1, 51, 1, /* 96 - 103 */ 8, 3, 2, 1, 54, 1, 55, 3, /* 104 - 111 */ 16, 1, 57, 133, 4, 137, 2, 135, /* 112 - 119 */ 60, 1, 61, 3, 62, 133, 63, 1, /* 120 - 127 */ 130, 1, 2, 1, 130, 1, 2, 131, /* 128 - 135 */ 130, 1, 2, 1, 130, 1, 2, 139, /* 136 - 143 */ 130, 1, 2, 131, 130, 1, 30, 1, /* 144 - 151 */ 130, 137, 2, 31, 130, 1, 2, 131, /* 152 - 159 */ 130, 1, 130, 1, 130, 133, 2, 1, /* 160 - 167 */ 130, 1, 130, 1, 2, 1, 130, 133, /* 168 - 175 */ 130, 1, 2, 1, 130, 1, 2, 61, /* 176 - 183 */ 130, 133, 62, 139, 130, 137, 130, 1, /* 184 - 191 */ 130, 1, 2, 131, 130, 1, 130, 1, /* 192 - 199 */ 130, 1, 2, 1, 130, 1, 2, 131, /* 200 - 207 */ 130, 1, 130, 1, 130, 131, 2, 133, /* 208 - 215 */ 130, 1, 2, 131, 130, 141, 130, 1, /* 216 - 223 */ 130, 133, 2, 1, 130, 1, 5, 135, /* 224 - 231 */ 130, 1, 130, 1, 2, 131, 130, 1, /* 232 - 239 */ 130, 1, 2, 131, 130, 133, 130, 141, /* 240 - 247 */ 130, 131, 130, 1, 130, 1, 2, 131 /* 248 - 255 */ }; /* as in gcc, but custom-optimized for stack calculation */ } static const int POWI_CACHE_SIZE = 256; #define FPO(x) /**/ //#define FPO(x) x //#include namespace { class PowiCache { private: int cache[POWI_CACHE_SIZE]; int cache_needed[POWI_CACHE_SIZE]; public: PowiCache() : cache(), cache_needed() /* Assume we have no factors in the cache */ { /* Decide which factors we would need multiple times. * Output: * cache[] = these factors were generated * cache_needed[] = number of times these factors were desired */ cache[1] = 1; // We have this value already. } bool Plan_Add(long value, int count) { if(value >= POWI_CACHE_SIZE) return false; //FPO(fprintf(stderr, "%ld will be needed %d times more\n", count, need_count)); cache_needed[value] += count; return cache[value] != 0; } void Plan_Has(long value) { if(value < POWI_CACHE_SIZE) cache[value] = 1; // This value has been generated } void Start(size_t value1_pos) { for(int n=2; n= 0) { // found from the cache FPO(fprintf(stderr, "* I found %ld from cache (%u,%d)\n", value, (unsigned)cache[value], cache_needed[value])); return cache[value]; } } return -1; } void Remember(long value, size_t stackpos) { if(value >= POWI_CACHE_SIZE) return; FPO(fprintf(stderr, "* Remembering that %ld can be found at %u (%d uses remain)\n", value, (unsigned)stackpos, cache_needed[value])); cache[value] = (int) stackpos; } void DumpContents() const { FPO(for(int a=1; a= 0 || cache_needed[a] > 0) { fprintf(stderr, "== cache: sp=%d, val=%d, needs=%d\n", cache[a], a, cache_needed[a]); }) } int UseGetNeeded(long value) { if(value >= 0 && value < POWI_CACHE_SIZE) return --cache_needed[value]; return 0; } }; template size_t AssembleSequence_Subdivide( long count, PowiCache& cache, const SequenceOpCode& sequencing, ByteCodeSynth& synth); template void Subdivide_Combine( size_t apos, long aval, size_t bpos, long bval, PowiCache& cache, unsigned cumulation_opcode, unsigned cimulation_opcode_flip, ByteCodeSynth& synth); void PlanNtimesCache (long value, PowiCache& cache, int need_count, int recursioncount=0) { if(value < 1) return; #ifdef FP_GENERATING_POWI_TABLE if(recursioncount > 32) throw false; #endif if(cache.Plan_Add(value, need_count)) return; long half = 1; if(value < POWI_TABLE_SIZE) { half = powi_table[value]; if(half & 128) { half &= 127; if(half & 64) half = -(half & 63) - 1; FPO(fprintf(stderr, "value=%ld, half=%ld, otherhalf=%ld\n", value,half,value/half)); PlanNtimesCache(half, cache, 1, recursioncount+1); cache.Plan_Has(half); return; } else if(half & 64) { half = -(half & 63) - 1; } } else if(value & 1) half = value & ((1 << POWI_WINDOW_SIZE) - 1); // that is, value & 7 else half = value / 2; long otherhalf = value-half; if(half > otherhalf || half<0) std::swap(half,otherhalf); FPO(fprintf(stderr, "value=%ld, half=%ld, otherhalf=%ld\n", value,half,otherhalf)); if(half == otherhalf) { PlanNtimesCache(half, cache, 2, recursioncount+1); } else { PlanNtimesCache(half, cache, 1, recursioncount+1); PlanNtimesCache(otherhalf>0?otherhalf:-otherhalf, cache, 1, recursioncount+1); } cache.Plan_Has(value); } template size_t AssembleSequence_Subdivide( long value, PowiCache& cache, const SequenceOpCode& sequencing, ByteCodeSynth& synth) { int cachepos = cache.Find(value); if(cachepos >= 0) { // found from the cache return cachepos; } long half = 1; if(value < POWI_TABLE_SIZE) { half = powi_table[value]; if(half & 128) { half &= 127; if(half & 64) half = -(half & 63) - 1; FPO(fprintf(stderr, "* I want %ld, my plan is %ld * %ld\n", value, half, value/half)); size_t half_pos = AssembleSequence_Subdivide(half, cache, sequencing, synth); if(cache.UseGetNeeded(half) > 0 || half_pos != synth.GetStackTop()-1) { synth.DoDup(half_pos); cache.Remember(half, synth.GetStackTop()-1); } AssembleSequence(value/half, sequencing, synth); size_t stackpos = synth.GetStackTop()-1; cache.Remember(value, stackpos); cache.DumpContents(); return stackpos; } else if(half & 64) { half = -(half & 63) - 1; } } else if(value & 1) half = value & ((1 << POWI_WINDOW_SIZE) - 1); // that is, value & 7 else half = value / 2; long otherhalf = value-half; if(half > otherhalf || half<0) std::swap(half,otherhalf); FPO(fprintf(stderr, "* I want %ld, my plan is %ld + %ld\n", value, half, value-half)); if(half == otherhalf) { size_t half_pos = AssembleSequence_Subdivide(half, cache, sequencing, synth); // self-cumulate the subdivide result Subdivide_Combine(half_pos,half, half_pos,half, cache, sequencing.op_normal, sequencing.op_normal_flip, synth); } else { long part1 = half; long part2 = otherhalf>0?otherhalf:-otherhalf; size_t part1_pos = AssembleSequence_Subdivide(part1, cache, sequencing, synth); size_t part2_pos = AssembleSequence_Subdivide(part2, cache, sequencing, synth); FPO(fprintf(stderr, "Subdivide(%ld: %ld, %ld)\n", value, half, otherhalf)); Subdivide_Combine(part1_pos,part1, part2_pos,part2, cache, otherhalf>0 ? sequencing.op_normal : sequencing.op_inverse, otherhalf>0 ? sequencing.op_normal_flip : sequencing.op_inverse_flip, synth); } size_t stackpos = synth.GetStackTop()-1; cache.Remember(value, stackpos); cache.DumpContents(); return stackpos; } template void Subdivide_Combine( size_t apos, long aval, size_t bpos, long bval, PowiCache& cache, unsigned cumulation_opcode, unsigned cumulation_opcode_flip, ByteCodeSynth& synth) { /*FPO(fprintf(stderr, "== making result for (sp=%u, val=%d, needs=%d) and (sp=%u, val=%d, needs=%d), stacktop=%u\n", (unsigned)apos, aval, aval>=0 ? cache_needed[aval] : -1, (unsigned)bpos, bval, bval>=0 ? cache_needed[bval] : -1, (unsigned)synth.GetStackTop()));*/ // Figure out whether we can trample a and b int a_needed = cache.UseGetNeeded(aval); int b_needed = cache.UseGetNeeded(bval); bool flipped = false; #define DUP_BOTH() do { \ if(apos < bpos) { size_t tmp=apos; apos=bpos; bpos=tmp; flipped=!flipped; } \ FPO(fprintf(stderr, "-> dup(%u) dup(%u) op\n", (unsigned)apos, (unsigned)bpos)); \ synth.DoDup(apos); \ synth.DoDup(apos==bpos ? synth.GetStackTop()-1 : bpos); } while(0) #define DUP_ONE(p) do { \ FPO(fprintf(stderr, "-> dup(%u) op\n", (unsigned)p)); \ synth.DoDup(p); \ } while(0) if(a_needed > 0) { if(b_needed > 0) { // If they must both be preserved, make duplicates // First push the one that is at the larger stack // address. This increases the odds of possibly using cDup. DUP_BOTH(); //SCENARIO 1: // Input: x B A x x // Temp: x B A x x A B // Output: x B A x x R //SCENARIO 2: // Input: x A B x x // Temp: x A B x x B A // Output: x A B x x R } else { // A must be preserved, but B can be trampled over // SCENARIO 1: // Input: x B x x A // Temp: x B x x A A B (dup both, later first) // Output: x B x x A R // SCENARIO 2: // Input: x A x x B // Temp: x A x x B A // Output: x A x x R -- only commutative cases // SCENARIO 3: // Input: x x x B A // Temp: x x x B A A B (dup both, later first) // Output: x x x B A R // SCENARIO 4: // Input: x x x A B // Temp: x x x A B A -- only commutative cases // Output: x x x A R // SCENARIO 5: // Input: x A B x x // Temp: x A B x x A B (dup both, later first) // Output: x A B x x R // if B is not at the top, dup both. if(bpos != synth.GetStackTop()-1) DUP_BOTH(); // dup both else { DUP_ONE(apos); // just dup A flipped=!flipped; } } } else if(b_needed > 0) { // B must be preserved, but A can be trampled over // This is a mirror image of the a_needed>0 case, so I'll cut the chase if(apos != synth.GetStackTop()-1) DUP_BOTH(); else DUP_ONE(bpos); } else { // Both can be trampled over. // SCENARIO 1: // Input: x B x x A // Temp: x B x x A B // Output: x B x x R // SCENARIO 2: // Input: x A x x B // Temp: x A x x B A // Output: x A x x R -- only commutative cases // SCENARIO 3: // Input: x x x B A // Output: x x x R -- only commutative cases // SCENARIO 4: // Input: x x x A B // Output: x x x R // SCENARIO 5: // Input: x A B x x // Temp: x A B x x A B (dup both, later first) // Output: x A B x x R // SCENARIO 6: // Input: x x x C // Temp: x x x C C (c is both A and B) // Output: x x x R if(apos == bpos && apos == synth.GetStackTop()-1) DUP_ONE(apos); // scenario 6 else if(apos == synth.GetStackTop()-1 && bpos == synth.GetStackTop()-2) { FPO(fprintf(stderr, "-> op\n")); // scenario 3 flipped=!flipped; } else if(apos == synth.GetStackTop()-2 && bpos == synth.GetStackTop()-1) FPO(fprintf(stderr, "-> op\n")); // scenario 4 else if(apos == synth.GetStackTop()-1) DUP_ONE(bpos); // scenario 1 else if(bpos == synth.GetStackTop()-1) { DUP_ONE(apos); // scenario 2 flipped=!flipped; } else DUP_BOTH(); // scenario 5 } // Add them together. synth.AddOperation(flipped ? cumulation_opcode_flip : cumulation_opcode, 2); } template void LightWeight( long count, const SequenceOpCode& sequencing, ByteCodeSynth& synth) { while(count < 256) { int half = FPoptimizer_ByteCode::powi_table[count]; if(half & 128) { half &= 127; LightWeight(half, sequencing, synth); count /= half; } else break; } if(count == 1) return; if(!(count & 1)) { synth.AddOperation(cSqr, 1); LightWeight(count/2, sequencing, synth); } else { synth.DoDup(synth.GetStackTop()-1); LightWeight(count-1, sequencing, synth); synth.AddOperation(cMul, 2); } } } namespace FPoptimizer_ByteCode { template void AssembleSequence( long count, const SequenceOpCode& sequencing, ByteCodeSynth& synth) { if(count == 0) synth.PushImmed(sequencing.basevalue); else { bool needs_flip = false; if(count < 0) { needs_flip = true; count = -count; } if(false) LightWeight(count,sequencing,synth); else if(count > 1) { /* To prevent calculating the same factors over and over again, * we use a cache. */ PowiCache cache; PlanNtimesCache(count, cache, 1); size_t stacktop_desired = synth.GetStackTop(); cache.Start( synth.GetStackTop()-1 ); FPO(fprintf(stderr, "Calculating result for %ld...\n", count)); size_t res_stackpos = AssembleSequence_Subdivide( count, cache, sequencing, synth); size_t n_excess = synth.GetStackTop() - stacktop_desired; if(n_excess > 0 || res_stackpos != stacktop_desired-1) { // Remove the cache values synth.DoPopNMov(stacktop_desired-1, res_stackpos); } } if(needs_flip) synth.AddOperation(sequencing.op_flip, 1); } } } /* BEGIN_EXPLICIT_INSTANTATION */ #include "instantiate.hh" namespace FPoptimizer_ByteCode { #define FP_INSTANTIATE(type) \ template struct SequenceOpcodes; \ template void ByteCodeSynth::AddFunctionOpcode(unsigned); \ template void ByteCodeSynth::AddFunctionOpcode(unsigned, \ Specializer< bool(FUNCTIONPARSERTYPES::IsIntType::result), \ bool(FUNCTIONPARSERTYPES::IsComplexType::result) \ > ); \ template void AssembleSequence( \ long count, \ const SequenceOpCode& sequencing, \ ByteCodeSynth& synth); FPOPTIMIZER_EXPLICITLY_INSTANTIATE(FP_INSTANTIATE) #undef FP_INSTANTIATE } /* END_EXPLICIT_INSTANTATION */ #endif fparserc++-4.5.2/fpoptimizer/bytecodesynth.hh000066400000000000000000000243121332731714600212430ustar00rootroot00000000000000#include "fpconfig.hh" #include "fparser.hh" #include "extrasrc/fptypes.hh" #ifdef FP_SUPPORT_OPTIMIZER #include #include #include "codetree.hh" #ifndef FP_GENERATING_POWI_TABLE enum { MAX_POWI_BYTECODE_LENGTH = 20 }; #else enum { MAX_POWI_BYTECODE_LENGTH = 999 }; #endif enum { MAX_MULI_BYTECODE_LENGTH = 3 }; namespace FPoptimizer_ByteCode { template class ByteCodeSynth { public: ByteCodeSynth() : ByteCode(), Immed(), StackState(), StackTop(0), StackMax(0) { /* estimate the initial requirements as such */ ByteCode.reserve(64); Immed.reserve(8); StackState.reserve(16); } void Pull(std::vector& bc, std::vector& imm, size_t& StackTop_max) { /* The bitmask 0x80000000u was added to each non-opcode * value within ByteCode[] (opcode parameters) to prevent * them being interpreted as opcodes by fp_opcode_add.inc. * fparser uses cNop for the same purpose. */ for(unsigned a=0; a& tree, int offset = 0) { if((int)StackTop > offset) { StackState[StackTop-1-offset].first = true; StackState[StackTop-1-offset].second = tree; } } bool IsStackTop(const FPoptimizer_CodeTree::CodeTree& tree, int offset = 0) const { return (int)StackTop > offset && StackState[StackTop-1-offset].first && StackState[StackTop-1-offset].second.IsIdenticalTo(tree); } inline void EatNParams(unsigned eat_count) { StackTop -= eat_count; } void ProducedNParams(unsigned produce_count) { SetStackTop(StackTop + produce_count); } void DoPopNMov(size_t targetpos, size_t srcpos) { using namespace FUNCTIONPARSERTYPES; ByteCode.push_back(cPopNMov); ByteCode.push_back( 0x80000000u | (unsigned) targetpos); ByteCode.push_back( 0x80000000u | (unsigned) srcpos); SetStackTop(srcpos+1); StackState[targetpos] = StackState[srcpos]; SetStackTop(targetpos+1); } void DoDup(size_t src_pos) { using namespace FUNCTIONPARSERTYPES; if(src_pos == StackTop-1) { ByteCode.push_back(cDup); } else { ByteCode.push_back(cFetch); ByteCode.push_back( 0x80000000u | (unsigned) src_pos); } SetStackTop(StackTop + 1); StackState[StackTop-1] = StackState[src_pos]; } #ifdef FUNCTIONPARSER_SUPPORT_DEBUGGING template void Dump() { std::ostream& o = std::cout; o << "Stack state now(" << StackTop << "):\n"; for(size_t a=0; a & tree = StackState[a].second; o << '[' << std::hex << (void*)(&tree.GetParams()) << std::dec << ',' << tree.GetRefCount() << ']'; DumpTree(tree, o); } else o << "?"; o << "\n"; } o << std::flush; } #endif size_t FindPos(const FPoptimizer_CodeTree::CodeTree& tree) const { for(size_t a=StackTop; a-->0; ) if(StackState[a].first && StackState[a].second.IsIdenticalTo(tree)) return a; return ~size_t(0); } bool Find(const FPoptimizer_CodeTree::CodeTree& tree) const { return FindPos(tree) != ~size_t(0); } bool FindAndDup(const FPoptimizer_CodeTree::CodeTree& tree) { size_t pos = FindPos(tree); if(pos != ~size_t(0)) { #ifdef DEBUG_SUBSTITUTIONS std::cout << "Found duplicate at [" << pos <<"]: "; DumpTree(tree); std::cout << " -- issuing cDup or cFetch\n"; #endif DoDup(pos); return true; } return false; } struct IfData { size_t ofs; }; void SynthIfStep1(IfData& ifdata, FUNCTIONPARSERTYPES::OPCODE op) { using namespace FUNCTIONPARSERTYPES; SetStackTop(StackTop-1); // the If condition was popped. ifdata.ofs = ByteCode.size(); ByteCode.push_back(op); ByteCode.push_back(0x80000000u); // code index ByteCode.push_back(0x80000000u); // Immed index } void SynthIfStep2(IfData& ifdata) { using namespace FUNCTIONPARSERTYPES; SetStackTop(StackTop-1); // ignore the pushed then-branch result. ByteCode[ifdata.ofs+1] = 0x80000000u | unsigned( ByteCode.size()+2 ); ByteCode[ifdata.ofs+2] = 0x80000000u | unsigned( Immed.size() ); ifdata.ofs = ByteCode.size(); ByteCode.push_back(cJump); ByteCode.push_back(0x80000000u); // code index ByteCode.push_back(0x80000000u); // Immed index } void SynthIfStep3(IfData& ifdata) { using namespace FUNCTIONPARSERTYPES; SetStackTop(StackTop-1); // ignore the pushed else-branch result. ByteCode.back() |= 0x80000000u; // ^Necessary for guarding against if(x,1,2)+1 being changed // into if(x,1,3) by fp_opcode_add.inc ByteCode[ifdata.ofs+1] = 0x80000000u | unsigned( ByteCode.size()-1 ); ByteCode[ifdata.ofs+2] = 0x80000000u | unsigned( Immed.size() ); SetStackTop(StackTop+1); // one or the other was pushed. /* Threading jumps: * If there are any cJumps that point * to the cJump instruction we just changed, * change them to point to this target as well. * This screws up PrintByteCode() majorly. */ for(size_t a=0; a StackMax) { StackMax = StackTop; StackState.resize(StackMax); } } protected: std::vector ByteCode; std::vector Immed; std::vector< std::pair/*tree*/> > StackState; size_t StackTop; size_t StackMax; private: void incStackPtr() { if(StackTop+2 > StackMax) StackState.resize(StackMax=StackTop+2); } template struct Specializer { }; public: void AddOperation(unsigned opcode, unsigned eat_count, unsigned produce_count = 1) { EatNParams(eat_count); AddFunctionOpcode(opcode); ProducedNParams(produce_count); } void AddFunctionOpcode(unsigned opcode, Specializer); void AddFunctionOpcode(unsigned opcode, Specializer); void AddFunctionOpcode(unsigned opcode, Specializer); void AddFunctionOpcode(unsigned opcode, Specializer); inline void AddFunctionOpcode(unsigned opcode) { AddFunctionOpcode (opcode, Specializer< bool(FUNCTIONPARSERTYPES::IsIntType::result), bool(FUNCTIONPARSERTYPES::IsComplexType::result) > () ); } }; template struct SequenceOpCode; template struct SequenceOpcodes { /* Multiplication implemented with adds */ static const SequenceOpCode AddSequence; /* Exponentiation implemented with muls */ static const SequenceOpCode MulSequence; }; /* Generate a sequence that multiplies or exponentifies the * last operand in the stack by the given constant integer * amount (positive or negative). */ template void AssembleSequence( long count, const SequenceOpCode& sequencing, ByteCodeSynth& synth); } #endif fparserc++-4.5.2/fpoptimizer/codetree.cc000066400000000000000000000341061332731714600201410ustar00rootroot00000000000000#include #include #include "rangeestimation.hh" #include "optimize.hh" // for DEBUG_SUBSTITUTIONS #include "codetree.hh" #include "consts.hh" #ifdef FP_SUPPORT_OPTIMIZER using namespace FUNCTIONPARSERTYPES; //using namespace FPoptimizer_Grammar; namespace { #ifdef DEBUG_SUBSTITUTIONS void OutFloatHex(std::ostream& o, double d) { union { double d; uint_least64_t h; } data; data.d = d; o << "(" << std::hex << data.h << std::dec << ")"; } #ifdef FP_SUPPORT_FLOAT_TYPE void OutFloatHex(std::ostream& o, float f) { union { float f; uint_least32_t h; } data; data.f = f; o << "(" << std::hex << data.h << std::dec << ")"; } #endif #ifdef FP_SUPPORT_LONG_DOUBLE_TYPE void OutFloatHex(std::ostream& o, long double ld) { union { long double ld; struct { uint_least64_t a; unsigned short b; } s; } data; data.ld = ld; o << "(" << std::hex << data.s.b << data.s.a << std::dec << ")"; } #endif #ifdef FP_SUPPORT_LONG_INT_TYPE void OutFloatHex(std::ostream& o, long ld) { o << "(" << std::hex << ld << std::dec << ")"; } #endif #endif } namespace FPoptimizer_CodeTree { template CodeTree::CodeTree() : data(new CodeTreeData ()) // sets opcode to cNop { } template CodeTree::CodeTree(const Value_t& i, typename CodeTree::ImmedTag) : data(new CodeTreeData(i)) { data->Recalculate_Hash_NoRecursion(); } #if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L template CodeTree::CodeTree(Value_t&& i, typename CodeTree::ImmedTag) : data(new CodeTreeData(std::move(i))) { data->Recalculate_Hash_NoRecursion(); } #endif template CodeTree::CodeTree(unsigned v, typename CodeTree::VarTag) : data(new CodeTreeData (VarBegin, v)) { data->Recalculate_Hash_NoRecursion(); } template CodeTree::CodeTree(FUNCTIONPARSERTYPES::OPCODE o, typename CodeTree::OpcodeTag) : data(new CodeTreeData (o)) { data->Recalculate_Hash_NoRecursion(); } template CodeTree::CodeTree(FUNCTIONPARSERTYPES::OPCODE o, unsigned f, typename CodeTree::FuncOpcodeTag) : data(new CodeTreeData (o, f)) { data->Recalculate_Hash_NoRecursion(); } template CodeTree::CodeTree(const CodeTree& b, typename CodeTree::CloneTag) : data(new CodeTreeData(*b.data)) { } template CodeTree::~CodeTree() { } template void CodeTree::ReplaceWithImmed(const Value_t& i) { #ifdef DEBUG_SUBSTITUTIONS std::cout << "Replacing "; DumpTree(*this); if(IsImmed()) OutFloatHex(std::cout, GetImmed()); std::cout << " with const value " << i; OutFloatHex(std::cout, i); std::cout << "\n"; #endif data = new CodeTreeData (i); } template struct ParamComparer { bool operator() (const CodeTree& a, const CodeTree& b) const { if(a.GetDepth() != b.GetDepth()) return a.GetDepth() < b.GetDepth(); return a.GetHash() < b.GetHash(); } }; template void CodeTreeData::Sort() { /* If the tree is commutative, order the parameters * in a set order in order to make equality tests * efficient in the optimizer */ switch(Opcode) { case cAdd: case cMul: case cMin: case cMax: case cAnd: case cAbsAnd: case cOr: case cAbsOr: case cHypot: case cEqual: case cNEqual: std::sort(Params.begin(), Params.end(), ParamComparer()); break; case cLess: if(ParamComparer() (Params[1], Params[0])) { std::swap(Params[0], Params[1]); Opcode = cGreater; } break; case cLessOrEq: if(ParamComparer() (Params[1], Params[0])) { std::swap(Params[0], Params[1]); Opcode = cGreaterOrEq; } break; case cGreater: if(ParamComparer() (Params[1], Params[0])) { std::swap(Params[0], Params[1]); Opcode = cLess; } break; case cGreaterOrEq: if(ParamComparer() (Params[1], Params[0])) { std::swap(Params[0], Params[1]); Opcode = cLessOrEq; } break; /* case cDiv: if(ParamComparer() (Params[1], Params[0])) { std::swap(Params[0], Params[1]); Opcode = cRDiv; } break; case cRDiv: if(ParamComparer() (Params[1], Params[0])) { std::swap(Params[0], Params[1]); Opcode = cDiv; } break; case cSub: if(ParamComparer() (Params[1], Params[0])) { std::swap(Params[0], Params[1]); Opcode = cRSub; } break; case cRSub: if(ParamComparer() (Params[1], Params[0])) { std::swap(Params[0], Params[1]); Opcode = cSub; } break; */ default: break; } } template void CodeTree::AddParam(const CodeTree& param) { //std::cout << "AddParam called\n"; data->Params.push_back(param); } template void CodeTree::AddParamMove(CodeTree& param) { data->Params.push_back(CodeTree()); data->Params.back().swap(param); } template void CodeTree::SetParam(size_t which, const CodeTree& b) { DataP slot_holder ( data->Params[which].data ); data->Params[which] = b; } template void CodeTree::SetParamMove(size_t which, CodeTree& b) { DataP slot_holder ( data->Params[which].data ); data->Params[which].swap(b); } template void CodeTree::AddParams(const std::vector >& RefParams) { data->Params.insert(data->Params.end(), RefParams.begin(), RefParams.end()); } template void CodeTree::AddParamsMove(std::vector >& RefParams) { size_t endpos = data->Params.size(), added = RefParams.size(); data->Params.resize(endpos + added, CodeTree()); for(size_t p=0; pParams[endpos+p].swap( RefParams[p] ); } template void CodeTree::AddParamsMove(std::vector >& RefParams, size_t replacing_slot) { DataP slot_holder ( data->Params[replacing_slot].data ); DelParam(replacing_slot); AddParamsMove(RefParams); /* const size_t n_added = RefParams.size(); const size_t oldsize = data->Params.size(); const size_t newsize = oldsize + n_added - 1; if(RefParams.empty()) DelParam(replacing_slot); else { // 0 1 2 3 4 5 6 7 8 9 10 11 // a a a a X b b b b b // a a a a Y Y Y b b b b b // // replacing_slot = 4 // n_added = 3 // oldsize = 10 // newsize = 12 // tail_length = 5 data->Params.resize(newsize); data->Params[replacing_slot].data = 0; const size_t tail_length = oldsize - replacing_slot -1; for(size_t tail=0; tailParams[newsize-1-tail].data.UnsafeSetP( &*data->Params[newsize-1-tail-(n_added-1)].data); for(size_t head=1; headParams[replacing_slot+head].data.UnsafeSetP( 0 ); for(size_t p=0; pParams[replacing_slot+p].swap( RefParams[p] ); } */ } template void CodeTree::SetParams(const std::vector >& RefParams) { //std::cout << "SetParams called" << (do_clone ? ", clone" : ", no clone") << "\n"; std::vector > tmp(RefParams); data->Params.swap(tmp); } template void CodeTree::SetParamsMove(std::vector >& RefParams) { data->Params.swap(RefParams); RefParams.clear(); } #if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L template void CodeTree::SetParams(std::vector >&& RefParams) { //std::cout << "SetParams&& called\n"; SetParamsMove(RefParams); } #endif template void CodeTree::DelParam(size_t index) { std::vector >& Params = data->Params; //std::cout << "DelParam(" << index << ") called\n"; #if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L /* rvalue reference semantics makes this optimal */ Params.erase( Params.begin() + index ); #else /* This labor evades the need for refcount +1/-1 shuffling */ Params[index].data = 0; for(size_t p=index; p+1 void CodeTree::DelParams() { data->Params.clear(); } template bool CodeTree::IsIdenticalTo(const CodeTree& b) const { //if(data.isnull() != b.data.isnull()) return false; if(data.get() == b.data.get()) return true; return data->IsIdenticalTo(*b.data); } template bool CodeTreeData::IsIdenticalTo(const CodeTreeData& b) const { if(Hash != b.Hash) return false; // a quick catch-all if(Opcode != b.Opcode) return false; switch(Opcode) { case cImmed: return fp_equal(Value, b.Value); case VarBegin: return Var_or_Funcno == b.Var_or_Funcno; case cFCall: case cPCall: if(Var_or_Funcno != b.Var_or_Funcno) return false; break; default: break; } if(Params.size() != b.Params.size()) return false; for(size_t a=0; a void CodeTree::Become(const CodeTree& b) { if(&b != this && data.get() != b.data.get()) { DataP tmp = b.data; CopyOnWrite(); data.swap(tmp); } } template void CodeTree::CopyOnWrite() { if(GetRefCount() > 1) data = new CodeTreeData(*data); } template CodeTree CodeTree::GetUniqueRef() { if(GetRefCount() > 1) return CodeTree(*this, CloneTag()); return *this; } template CodeTreeData::CodeTreeData() : RefCount(0), Opcode(cNop), Value(), Var_or_Funcno(), Params(), Hash(), Depth(1), OptimizedUsing(0) { } template CodeTreeData::CodeTreeData(const CodeTreeData& b) : RefCount(0), Opcode(b.Opcode), Value(b.Value), Var_or_Funcno(b.Var_or_Funcno), Params(b.Params), Hash(b.Hash), Depth(b.Depth), OptimizedUsing(b.OptimizedUsing) { } template CodeTreeData::CodeTreeData(const Value_t& i) : RefCount(0), Opcode(cImmed), Value(i), Var_or_Funcno(), Params(), Hash(), Depth(1), OptimizedUsing(0) { } #if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L template CodeTreeData::CodeTreeData(CodeTreeData&& b) : RefCount(0), Opcode(b.Opcode), Value(std::move(b.Value)), Var_or_Funcno(b.Var_or_Funcno), Params(std::move(b.Params)), Hash(b.Hash), Depth(b.Depth), OptimizedUsing(b.OptimizedUsing) { } template CodeTreeData::CodeTreeData(Value_t&& i) : RefCount(0), Opcode(cImmed), Value(std::move(i)), Var_or_Funcno(), Params(), Hash(), Depth(1), OptimizedUsing(0) { } #endif template CodeTreeData::CodeTreeData(FUNCTIONPARSERTYPES::OPCODE o) : RefCount(0), Opcode(o), Value(), Var_or_Funcno(), Params(), Hash(), Depth(1), OptimizedUsing(0) { } template CodeTreeData::CodeTreeData(FUNCTIONPARSERTYPES::OPCODE o, unsigned f) : RefCount(0), Opcode(o), Value(), Var_or_Funcno(f), Params(), Hash(), Depth(1), OptimizedUsing(0) { } } /* BEGIN_EXPLICIT_INSTANTATION */ #include "instantiate.hh" namespace FPoptimizer_CodeTree { #define FP_INSTANTIATE(type) \ template class CodeTree; \ template struct CodeTreeData; FPOPTIMIZER_EXPLICITLY_INSTANTIATE(FP_INSTANTIATE) #undef FP_INSTANTIATE } /* END_EXPLICIT_INSTANTATION */ #endif fparserc++-4.5.2/fpoptimizer/codetree.hh000066400000000000000000000217071332731714600201560ustar00rootroot00000000000000#ifndef FPOptimizer_CodeTreeHH #define FPOptimizer_CodeTreeHH #include "fpconfig.hh" #include "fparser.hh" #include "extrasrc/fptypes.hh" #include "extrasrc/fpaux.hh" #ifdef FP_SUPPORT_OPTIMIZER #include #include #include "hash.hh" #include "../lib/autoptr.hh" namespace FPoptimizer_Grammar { struct Grammar; } namespace FPoptimizer_ByteCode { template class ByteCodeSynth; } namespace FPoptimizer_CodeTree { template class CodeTree; template struct CodeTreeData; template class CodeTree { typedef FPOPT_autoptr > DataP; DataP data; public: CodeTree(); ~CodeTree(); struct OpcodeTag { }; explicit CodeTree(FUNCTIONPARSERTYPES::OPCODE o, OpcodeTag); // produce an opcode struct FuncOpcodeTag { }; explicit CodeTree(FUNCTIONPARSERTYPES::OPCODE o, unsigned f, FuncOpcodeTag); struct ImmedTag { }; explicit CodeTree(const Value_t& v, ImmedTag); // produce an immed #if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L explicit CodeTree(Value_t&& v, ImmedTag); // produce an immed #endif struct VarTag { }; explicit CodeTree(unsigned varno, VarTag); // produce a var reference struct CloneTag { }; explicit CodeTree(const CodeTree& b, CloneTag); /* Generates a CodeTree from the given bytecode */ void GenerateFrom( const typename FunctionParserBase::Data& data, bool keep_powi = false); void GenerateFrom( const typename FunctionParserBase::Data& data, const std::vector& var_trees, bool keep_powi = false); void SynthesizeByteCode( std::vector& byteCode, std::vector& immed, size_t& stacktop_max); void SynthesizeByteCode( FPoptimizer_ByteCode::ByteCodeSynth& synth, bool MustPopTemps=true) const; size_t SynthCommonSubExpressions( FPoptimizer_ByteCode::ByteCodeSynth& synth) const; void SetParams(const std::vector& RefParams); void SetParamsMove(std::vector& RefParams); CodeTree GetUniqueRef(); // ^use this when CodeTree tmp=x; tmp.CopyOnWrite(); does not do exactly what you want #if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L void SetParams(std::vector&& RefParams); #endif void SetParam(size_t which, const CodeTree& b); void SetParamMove(size_t which, CodeTree& b); void AddParam(const CodeTree& param); void AddParamMove(CodeTree& param); void AddParams(const std::vector& RefParams); void AddParamsMove(std::vector& RefParams); void AddParamsMove(std::vector& RefParams, size_t replacing_slot); void DelParam(size_t index); void DelParams(); void Become(const CodeTree& b); inline size_t GetParamCount() const { return GetParams().size(); } inline CodeTree& GetParam(size_t n) { return GetParams()[n]; } inline const CodeTree& GetParam(size_t n) const { return GetParams()[n]; } inline void SetOpcode(FUNCTIONPARSERTYPES::OPCODE o) { data->Opcode = o; } inline FUNCTIONPARSERTYPES::OPCODE GetOpcode() const { return data->Opcode; } inline FUNCTIONPARSERTYPES::fphash_t GetHash() const { return data->Hash; } inline const std::vector& GetParams() const { return data->Params; } inline std::vector& GetParams() { return data->Params; } inline size_t GetDepth() const { return data->Depth; } inline const Value_t& GetImmed() const { return data->Value; } inline unsigned GetVar() const { return data->Var_or_Funcno; } inline unsigned GetFuncNo() const { return data->Var_or_Funcno; } inline bool IsDefined() const { return GetOpcode() != FUNCTIONPARSERTYPES::cNop; } inline bool IsImmed() const { return GetOpcode() == FUNCTIONPARSERTYPES::cImmed; } inline bool IsVar() const { return GetOpcode() == FUNCTIONPARSERTYPES::VarBegin; } inline unsigned GetRefCount() const { return data->RefCount; } void ReplaceWithImmed(const Value_t& i); void Rehash(bool constantfolding = true); void Sort(); inline void Mark_Incompletely_Hashed() { data->Depth=0; } inline bool Is_Incompletely_Hashed() const { return data->Depth == 0; } inline const FPoptimizer_Grammar::Grammar* GetOptimizedUsing() const { return data->OptimizedUsing; } inline void SetOptimizedUsing(const FPoptimizer_Grammar::Grammar* g) { data->OptimizedUsing = g; } bool RecreateInversionsAndNegations(bool prefer_base2 = false); void FixIncompleteHashes(); void swap(CodeTree& b) { data.swap(b.data); } bool IsIdenticalTo(const CodeTree& b) const; void CopyOnWrite(); }; template struct CodeTreeData { int RefCount; /* Describing the codetree node */ FUNCTIONPARSERTYPES::OPCODE Opcode; Value_t Value; // In case of cImmed: value of the immed unsigned Var_or_Funcno; // In case of VarBegin: variable number // In case of cFCall or cPCall: function number // Parameters for the function // These use the sign: // For cAdd: operands to add together (0 to n) // sign indicates that the value is negated before adding (0-x) // For cMul: operands to multiply together (0 to n) // sign indicates that the value is inverted before multiplying (1/x) // For cAnd: operands to bitwise-and together (0 to n) // sign indicates that the value is inverted before anding (!x) // For cOr: operands to bitwise-or together (0 to n) // sign indicates that the value is inverted before orring (!x) // These don't use the sign (sign is always false): // For cMin: operands to select the minimum of // For cMax: operands to select the maximum of // For cImmed, not used // For VarBegin, not used // For cIf: operand 1 = condition, operand 2 = yes-branch, operand 3 = no-branch // For anything else: the parameters required by the operation/function std::vector > Params; /* Internal operation */ FUNCTIONPARSERTYPES::fphash_t Hash; size_t Depth; const FPoptimizer_Grammar::Grammar* OptimizedUsing; CodeTreeData(); CodeTreeData(const CodeTreeData& b); explicit CodeTreeData(FUNCTIONPARSERTYPES::OPCODE o); explicit CodeTreeData(FUNCTIONPARSERTYPES::OPCODE o, unsigned f); explicit CodeTreeData(const Value_t& i); #if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L explicit CodeTreeData(Value_t&& i); CodeTreeData(CodeTreeData&& b); #endif bool IsIdenticalTo(const CodeTreeData& b) const; void Sort(); void Recalculate_Hash_NoRecursion(); private: void operator=(const CodeTreeData& b); }; /* Utility functions for creating different kind of CodeTrees */ template static inline CodeTree CodeTreeImmed(const Value_t& i) { return CodeTree (i, typename CodeTree::ImmedTag()); } #if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L template static inline CodeTree CodeTreeImmed(Value_t&& i) { return CodeTree (std::move(i), typename CodeTree::ImmedTag()); } #endif template static inline CodeTree CodeTreeOp(FUNCTIONPARSERTYPES::OPCODE opcode) { return CodeTree (opcode, typename CodeTree::OpcodeTag()); } template static inline CodeTree CodeTreeFuncOp(FUNCTIONPARSERTYPES::OPCODE opcode, unsigned f) { return CodeTree (opcode, f, typename CodeTree::FuncOpcodeTag()); } template static inline CodeTree CodeTreeVar(unsigned varno) { return CodeTree (varno, typename CodeTree::VarTag()); } /* Debugging functions */ #ifdef FUNCTIONPARSER_SUPPORT_DEBUGGING template void DumpHashes(const CodeTree& tree, std::ostream& o = std::cout); template void DumpTree(const CodeTree& tree, std::ostream& o = std::cout); template void DumpTreeWithIndent(const CodeTree& tree, std::ostream& o = std::cout, const std::string& indent = "\\"); #endif } #endif #endif fparserc++-4.5.2/fpoptimizer/constantfolding.cc000066400000000000000000001221071332731714600215420ustar00rootroot00000000000000#include #include "fpconfig.hh" #include "fparser.hh" #include "extrasrc/fptypes.hh" #ifdef FP_SUPPORT_OPTIMIZER #include "codetree.hh" #include "optimize.hh" #include "consts.hh" #include #include "rangeestimation.hh" #include "constantfolding.hh" #include "logic_boolgroups.hh" /* ^For ConstantFolding_AndLogic() * ConstantFolding_OrLogic() * ConstantFolding_MulLogicItems() * ConstantFolding_AddLogicItems() */ #include "logic_collections.hh" /* ^For ConstantFolding_MulGrouping() * ConstantFolding_AddGrouping() */ #include "logic_ifoperations.hh" /* ^For ConstantFolding_IfOperations() */ #include "logic_powoperations.hh" /* ^For ConstantFolding_PowOperations() */ #include "logic_comparisons.hh" /* ^For ConstantFolding_Comparison() */ #define FP_MUL_COMBINE_EXPONENTS namespace { using namespace FUNCTIONPARSERTYPES; using namespace FPoptimizer_CodeTree; /*************************************/ /* ADOPTING SAME-TYPE CHILDREN */ /*************************************/ template static void AdoptChildrenWithSameOpcode(CodeTree& tree) { /* If the list contains another list of the same kind, assimilate it */ #ifdef DEBUG_SUBSTITUTIONS bool assimilated = false; #endif for(size_t a=tree.GetParamCount(); a-- > 0; ) if(tree.GetParam(a).GetOpcode() == tree.GetOpcode()) { #ifdef DEBUG_SUBSTITUTIONS if(!assimilated) { std::cout << "Before assimilation: "; DumpTree(tree); std::cout << "\n"; assimilated = true; } #endif // Assimilate its children and remove it tree.AddParamsMove(tree.GetParam(a).GetUniqueRef().GetParams(), a); } #ifdef DEBUG_SUBSTITUTIONS if(assimilated) { std::cout << "After assimilation: "; DumpTree(tree); std::cout << "\n"; } #endif } } namespace FPoptimizer_CodeTree { template void ConstantFolding(CodeTree& tree) { tree.Sort(); // Otherwise "0 <= acos(x)" does not get properly optimized #ifdef DEBUG_SUBSTITUTIONS void* stackptr=0; std::cout << "[" << (&stackptr) << "]Runs ConstantFolding for: "; DumpTree(tree); std::cout << "\n"; DumpHashes(tree); std::cout << std::flush; #endif if(false) { redo:; tree.Sort(); #ifdef DEBUG_SUBSTITUTIONS std::cout << "[" << (&stackptr) << "]Re-runs ConstantFolding: "; DumpTree(tree); std::cout << "\n"; DumpHashes(tree); #endif } // Insert here any hardcoded constant-folding optimizations // that you want to be done whenever a new subtree is generated. /* Not recursive. */ if(tree.GetOpcode() != cImmed) { range p = CalculateResultBoundaries(tree); if(p.min.known && p.max.known && p.min.val == p.max.val) { // Replace us with this immed tree.ReplaceWithImmed(p.min.val); goto do_return; } } if(false) { ReplaceTreeWithOne: tree.ReplaceWithImmed(Value_t(1)); goto do_return; ReplaceTreeWithZero: tree.ReplaceWithImmed(Value_t(0)); goto do_return; ReplaceTreeWithParam0: #ifdef DEBUG_SUBSTITUTIONS std::cout << "Before replace: "; std::cout << std::hex << '[' << tree.GetHash().hash1 << ',' << tree.GetHash().hash2 << ']' << std::dec; DumpTree(tree); std::cout << "\n"; #endif tree.Become(tree.GetParam(0)); #ifdef DEBUG_SUBSTITUTIONS std::cout << "After replace: "; std::cout << std::hex << '[' << tree.GetHash().hash1 << ',' << tree.GetHash().hash2 << ']' << std::dec; DumpTree(tree); std::cout << "\n"; #endif goto redo; } /* Constant folding */ switch(tree.GetOpcode()) { case cImmed: break; // nothing to do case VarBegin: break; // nothing to do case cAnd: case cAbsAnd: { AdoptChildrenWithSameOpcode(tree); bool has_nonlogical_values = false; for(size_t a=tree.GetParamCount(); a-- > 0; ) { if(!IsLogicalValue(tree.GetParam(a))) has_nonlogical_values = true; switch(GetLogicalValue(tree.GetParam(a), tree.GetOpcode()==cAbsAnd)) { case IsNever: goto ReplaceTreeWithZero; case IsAlways: tree.DelParam(a); break; // x & y & 1 = x & y; x & 1 = !!x case Unknown: default: ; } } switch(tree.GetParamCount()) { case 0: goto ReplaceTreeWithOne; case 1: tree.SetOpcode(tree.GetOpcode()==cAnd ? cNotNot : cAbsNotNot); goto redo; // Replace self with the single operand default: if(tree.GetOpcode()==cAnd || !has_nonlogical_values) if(ConstantFolding_AndLogic(tree)) goto redo; } break; } case cOr: case cAbsOr: { AdoptChildrenWithSameOpcode(tree); bool has_nonlogical_values = false; for(size_t a=tree.GetParamCount(); a-- > 0; ) { if(!IsLogicalValue(tree.GetParam(a))) has_nonlogical_values = true; switch(GetLogicalValue(tree.GetParam(a), tree.GetOpcode()==cAbsOr)) { case IsAlways: goto ReplaceTreeWithOne; case IsNever: tree.DelParam(a); break; case Unknown: default: ; } } switch(tree.GetParamCount()) { case 0: goto ReplaceTreeWithZero; case 1: tree.SetOpcode(tree.GetOpcode()==cOr ? cNotNot : cAbsNotNot); goto redo; // Replace self with the single operand default: if(tree.GetOpcode()==cOr || !has_nonlogical_values) if(ConstantFolding_OrLogic(tree)) goto redo; } break; } case cNot: case cAbsNot: { unsigned opposite = 0; switch(tree.GetParam(0).GetOpcode()) { case cEqual: opposite = cNEqual; break; case cNEqual: opposite = cEqual; break; case cLess: opposite = cGreaterOrEq; break; case cGreater: opposite = cLessOrEq; break; case cLessOrEq: opposite = cGreater; break; case cGreaterOrEq: opposite = cLess; break; case cNotNot: opposite = cNot; break; case cNot: opposite = cNotNot; break; case cAbsNot: opposite = cAbsNotNot; break; case cAbsNotNot: opposite = cAbsNot; break; default: break; } if(opposite) { tree.SetOpcode(OPCODE(opposite)); tree.SetParamsMove(tree.GetParam(0).GetUniqueRef().GetParams()); goto redo; } // If the sub-expression evaluates to approx. zero, yield one. // If the sub-expression evaluates to approx. nonzero, yield zero. switch(GetLogicalValue(tree.GetParam(0), tree.GetOpcode()==cAbsNot)) { case IsAlways: goto ReplaceTreeWithZero; case IsNever: goto ReplaceTreeWithOne; case Unknown: default: ; } if(tree.GetOpcode() == cNot && GetPositivityInfo(tree.GetParam(0)) == IsAlways) tree.SetOpcode(cAbsNot); if(tree.GetParam(0).GetOpcode() == cIf || tree.GetParam(0).GetOpcode() == cAbsIf) { CodeTree iftree = tree.GetParam(0); const CodeTree& ifp1 = iftree.GetParam(1); const CodeTree& ifp2 = iftree.GetParam(2); if(ifp1.GetOpcode() == cNot || ifp1.GetOpcode() == cAbsNot) { // cNot [(cIf [x (cNot[y]) z])] -> cIf [x (cNotNot[y]) (cNot[z])] tree.SetParam(0, iftree.GetParam(0)); // condition CodeTree p1; p1.SetOpcode(ifp1.GetOpcode()==cNot ? cNotNot : cAbsNotNot); p1.AddParam(ifp1.GetParam(0)); p1.Rehash(); tree.AddParamMove(p1); CodeTree p2; p2.SetOpcode(tree.GetOpcode()); p2.AddParam(ifp2); p2.Rehash(); tree.AddParamMove(p2); tree.SetOpcode(iftree.GetOpcode()); goto redo; } if(ifp2.GetOpcode() == cNot || ifp2.GetOpcode() == cAbsNot) { // cNot [(cIf [x y (cNot[z])])] -> cIf [x (cNot[y]) (cNotNot[z])] tree.SetParam(0, iftree.GetParam(0)); // condition CodeTree p1; p1.SetOpcode(tree.GetOpcode()); p1.AddParam(ifp1); p1.Rehash(); tree.AddParamMove(p1); CodeTree p2; p2.SetOpcode(ifp2.GetOpcode()==cNot ? cNotNot : cAbsNotNot); p2.AddParam(ifp2.GetParam(0)); p2.Rehash(); tree.AddParamMove(p2); tree.SetOpcode(iftree.GetOpcode()); goto redo; } } break; } case cNotNot: case cAbsNotNot: { // The function of cNotNot is to protect a logical value from // changing. If the parameter is already a logical value, // then the cNotNot opcode is redundant. if(IsLogicalValue(tree.GetParam(0))) goto ReplaceTreeWithParam0; // If the sub-expression evaluates to approx. zero, yield zero. // If the sub-expression evaluates to approx. nonzero, yield one. switch(GetLogicalValue(tree.GetParam(0), tree.GetOpcode()==cAbsNotNot)) { case IsNever: goto ReplaceTreeWithZero; case IsAlways: goto ReplaceTreeWithOne; case Unknown: default: ; } if(tree.GetOpcode() == cNotNot && GetPositivityInfo(tree.GetParam(0)) == IsAlways) tree.SetOpcode(cAbsNotNot); if(tree.GetParam(0).GetOpcode() == cIf || tree.GetParam(0).GetOpcode() == cAbsIf) { CodeTree iftree = tree.GetParam(0); const CodeTree& ifp1 = iftree.GetParam(1); const CodeTree& ifp2 = iftree.GetParam(2); if(ifp1.GetOpcode() == cNot || ifp1.GetOpcode() == cAbsNot) { // cNotNot [(cIf [x (cNot[y]) z])] -> cIf [x (cNot[y]) (cNotNot[z])] tree.SetParam(0, iftree.GetParam(0)); // condition tree.AddParam(ifp1); CodeTree p2; p2.SetOpcode(tree.GetOpcode()); p2.AddParam(ifp2); p2.Rehash(); tree.AddParamMove(p2); tree.SetOpcode(iftree.GetOpcode()); goto redo; } if(ifp2.GetOpcode() == cNot || ifp2.GetOpcode() == cAbsNot) { // cNotNot [(cIf [x y (cNot[z])])] -> cIf [x (cNotNot[y]) (cNot[z])] tree.SetParam(0, iftree.GetParam(0)); // condition CodeTree p1; p1.SetOpcode(tree.GetOpcode()); p1.AddParam(ifp1); p1.Rehash(); tree.AddParamMove(p1); tree.AddParam(ifp2); tree.SetOpcode(iftree.GetOpcode()); goto redo; } } break; } case cIf: case cAbsIf: { if(ConstantFolding_IfOperations(tree)) goto redo; break; } case cMul: { NowWeAreMulGroup: ; AdoptChildrenWithSameOpcode(tree); // If one sub-expression evalutes to exact zero, yield zero. Value_t immed_product = Value_t(1); size_t n_immeds = 0; bool needs_resynth=false; for(size_t a=0; a 1 || (n_immeds == 1 && fp_equal(immed_product, Value_t(1)))) needs_resynth = true; if(needs_resynth) { // delete immeds and add new ones #ifdef DEBUG_SUBSTITUTIONS std::cout << "cMul: Will add new immed " << immed_product << "\n"; #endif for(size_t a=tree.GetParamCount(); a-->0; ) if(tree.GetParam(a).IsImmed()) { #ifdef DEBUG_SUBSTITUTIONS std::cout << " - For that, deleting immed " << tree.GetParam(a).GetImmed(); std::cout << "\n"; #endif tree.DelParam(a); } if(!fp_equal(immed_product, Value_t(1))) tree.AddParam( CodeTreeImmed (immed_product) ); } switch(tree.GetParamCount()) { case 0: goto ReplaceTreeWithOne; case 1: goto ReplaceTreeWithParam0; // Replace self with the single operand default: if(ConstantFolding_MulGrouping(tree)) goto redo; if(ConstantFolding_MulLogicItems(tree)) goto redo; } break; } case cAdd: { AdoptChildrenWithSameOpcode(tree); Value_t immed_sum = 0.0; size_t n_immeds = 0; bool needs_resynth=false; for(size_t a=0; a 1 || (n_immeds == 1 && immed_sum == Value_t(0))) needs_resynth = true; if(needs_resynth) { // delete immeds and add new ones #ifdef DEBUG_SUBSTITUTIONS std::cout << "cAdd: Will add new immed " << immed_sum << "\n"; std::cout << "In: "; DumpTree(tree); std::cout << "\n"; #endif for(size_t a=tree.GetParamCount(); a-->0; ) if(tree.GetParam(a).IsImmed()) { #ifdef DEBUG_SUBSTITUTIONS std::cout << " - For that, deleting immed " << tree.GetParam(a).GetImmed(); std::cout << "\n"; #endif tree.DelParam(a); } if(!(immed_sum == Value_t(0.0))) tree.AddParam( CodeTreeImmed (immed_sum) ); } switch(tree.GetParamCount()) { case 0: goto ReplaceTreeWithZero; case 1: goto ReplaceTreeWithParam0; // Replace self with the single operand default: if(ConstantFolding_AddGrouping(tree)) goto redo; if(ConstantFolding_AddLogicItems(tree)) goto redo; } break; } case cMin: { AdoptChildrenWithSameOpcode(tree); /* Goal: If there is any pair of two operands, where * their ranges form a disconnected set, i.e. as below: * xxxxx * yyyyyy * Then remove the larger one. * * Algorithm: 1. figure out the smallest maximum of all operands. * 2. eliminate all operands where their minimum is * larger than the selected maximum. */ size_t preserve=0; range smallest_maximum; for(size_t a=0; a p = CalculateResultBoundaries( tree.GetParam(a) ); if(p.max.known && (!smallest_maximum.max.known || (p.max.val) < smallest_maximum.max.val)) { smallest_maximum.max.val = p.max.val; smallest_maximum.max.known = true; preserve=a; } } if(smallest_maximum.max.known) for(size_t a=tree.GetParamCount(); a-- > 0; ) { range p = CalculateResultBoundaries( tree.GetParam(a) ); if(p.min.known && a != preserve && p.min.val >= smallest_maximum.max.val) tree.DelParam(a); } //fprintf(stderr, "Remains: %u\n", (unsigned)tree.GetParamCount()); if(tree.GetParamCount() == 1) { // Replace self with the single operand goto ReplaceTreeWithParam0; } break; } case cMax: { AdoptChildrenWithSameOpcode(tree); /* Goal: If there is any pair of two operands, where * their ranges form a disconnected set, i.e. as below: * xxxxx * yyyyyy * Then remove the smaller one. * * Algorithm: 1. figure out the biggest minimum of all operands. * 2. eliminate all operands where their maximum is * smaller than the selected minimum. */ size_t preserve=0; range biggest_minimum; for(size_t a=0; a p = CalculateResultBoundaries( tree.GetParam(a) ); if(p.min.known && (!biggest_minimum.min.known || p.min.val > biggest_minimum.min.val)) { biggest_minimum.min.val = p.min.val; biggest_minimum.min.known = true; preserve=a; } } if(biggest_minimum.min.known) { //fprintf(stderr, "Removing all where max < %g\n", biggest_minimum.min.val); for(size_t a=tree.GetParamCount(); a-- > 0; ) { range p = CalculateResultBoundaries( tree.GetParam(a) ); if(p.max.known && a != preserve && (p.max.val) < biggest_minimum.min.val) { //fprintf(stderr, "Removing %g\n", p.max.val); tree.DelParam(a); } } } //fprintf(stderr, "Remains: %u\n", (unsigned)tree.GetParamCount()); if(tree.GetParamCount() == 1) { // Replace self with the single operand goto ReplaceTreeWithParam0; } break; } case cEqual: case cNEqual: case cLess: case cGreater: case cLessOrEq: case cGreaterOrEq: if(ConstantFolding_Comparison(tree)) goto redo; break; case cAbs: { /* If we know the operand is always positive, cAbs is redundant. * If we know the operand is always negative, use actual negation. */ range p0 = CalculateResultBoundaries( tree.GetParam(0) ); if(p0.min.known && p0.min.val >= Value_t(0.0)) goto ReplaceTreeWithParam0; if(p0.max.known && p0.max.val <= fp_const_negativezero()) { /* abs(negative) = negative*-1 */ tree.SetOpcode(cMul); tree.AddParam( CodeTreeImmed(Value_t(1)) ); /* The caller of ConstantFolding() will do Sort() and Rehash() next. * Thus, no need to do it here. */ /* We were changed into a cMul group. Do cMul folding. */ goto NowWeAreMulGroup; } /* If the operand is a cMul group, find elements * that are always positive and always negative, * and move them out, e.g. abs(p*n*x*y) = p*(-n)*abs(x*y) */ if(tree.GetParam(0).GetOpcode() == cMul) { const CodeTree& p = tree.GetParam(0); std::vector > pos_set; std::vector > neg_set; for(size_t a=0; a= Value_t(0.0)) { pos_set.push_back(p.GetParam(a)); } if(p0.max.known && p0.max.val <= fp_const_negativezero()) { neg_set.push_back(p.GetParam(a)); } } #ifdef DEBUG_SUBSTITUTIONS std::cout << "Abs: mul group has " << pos_set.size() << " pos, " << neg_set.size() << "neg\n"; #endif if(!pos_set.empty() || !neg_set.empty()) { #ifdef DEBUG_SUBSTITUTIONS std::cout << "AbsReplace-Before: "; DumpTree(tree); std::cout << "\n" << std::flush; DumpHashes(tree, std::cout); #endif CodeTree pclone; pclone.SetOpcode(cMul); for(size_t a=0; a= Value_t(0.0)) || (p0.max.known && p0.max.val <= fp_const_negativezero())) {/*pclone.DelParam(a);*/} else pclone.AddParam( p.GetParam(a) ); /* Here, p*n*x*y -> x*y. * p is saved in pos_set[] * n is saved in neg_set[] */ } pclone.Rehash(); CodeTree abs_mul; abs_mul.SetOpcode(cAbs); abs_mul.AddParamMove(pclone); abs_mul.Rehash(); CodeTree mulgroup; mulgroup.SetOpcode(cMul); mulgroup.AddParamMove(abs_mul); // cAbs[whatever remains in p] mulgroup.AddParamsMove(pos_set); /* Now: * mulgroup = p * Abs(x*y) */ if(!neg_set.empty()) { if(neg_set.size() % 2) mulgroup.AddParam( CodeTreeImmed(Value_t(-1)) ); mulgroup.AddParamsMove(neg_set); /* Now: * mulgroup = p * n * -1 * Abs(x*y) */ } tree.Become(mulgroup); #ifdef DEBUG_SUBSTITUTIONS std::cout << "AbsReplace-After: "; DumpTree(tree, std::cout); std::cout << "\n" << std::flush; DumpHashes(tree, std::cout); #endif /* We were changed into a cMul group. Do cMul folding. */ goto NowWeAreMulGroup; } } break; } #define HANDLE_UNARY_CONST_FUNC(funcname) \ if(tree.GetParam(0).IsImmed()) \ { tree.ReplaceWithImmed( funcname(tree.GetParam(0).GetImmed()) ); \ goto do_return; } case cLog: HANDLE_UNARY_CONST_FUNC(fp_log); if(tree.GetParam(0).GetOpcode() == cPow) { CodeTree pow = tree.GetParam(0); if(GetPositivityInfo(pow.GetParam(0)) == IsAlways) // log(posi ^ y) = y*log(posi) { pow.CopyOnWrite(); pow.SetOpcode(cLog); tree.SetOpcode(cMul); tree.AddParamMove(pow.GetParam(1)); pow.DelParam(1); pow.Rehash(); tree.SetParamMove(0, pow); goto NowWeAreMulGroup; } if(GetEvennessInfo(pow.GetParam(1)) == IsAlways) // log(x ^ even) = even*log(abs(x)) { pow.CopyOnWrite(); CodeTree abs; abs.SetOpcode(cAbs); abs.AddParamMove(pow.GetParam(0)); abs.Rehash(); pow.SetOpcode(cLog); tree.SetOpcode(cMul); pow.SetParamMove(0, abs); tree.AddParamMove(pow.GetParam(1)); pow.DelParam(1); pow.Rehash(); tree.SetParamMove(0, pow); goto NowWeAreMulGroup; } } else if(tree.GetParam(0).GetOpcode() == cAbs) { // log(abs(x^y)) = y*log(abs(x)) CodeTree pow = tree.GetParam(0).GetParam(0); if(pow.GetOpcode() == cPow) { pow.CopyOnWrite(); CodeTree abs; abs.SetOpcode(cAbs); abs.AddParamMove(pow.GetParam(0)); abs.Rehash(); pow.SetOpcode(cLog); tree.SetOpcode(cMul); pow.SetParamMove(0, abs); tree.AddParamMove(pow.GetParam(1)); pow.DelParam(1); pow.Rehash(); tree.SetParamMove(0, pow); goto NowWeAreMulGroup; } } break; case cAcosh: HANDLE_UNARY_CONST_FUNC(fp_acosh); break; case cAsinh: HANDLE_UNARY_CONST_FUNC(fp_asinh); break; case cAtanh: HANDLE_UNARY_CONST_FUNC(fp_atanh); break; case cAcos: HANDLE_UNARY_CONST_FUNC(fp_acos); break; case cAsin: HANDLE_UNARY_CONST_FUNC(fp_asin); break; case cAtan: HANDLE_UNARY_CONST_FUNC(fp_atan); break; case cCosh: HANDLE_UNARY_CONST_FUNC(fp_cosh); break; case cSinh: HANDLE_UNARY_CONST_FUNC(fp_sinh); break; case cTanh: HANDLE_UNARY_CONST_FUNC(fp_tanh); break; case cSin: HANDLE_UNARY_CONST_FUNC(fp_sin); break; case cCos: HANDLE_UNARY_CONST_FUNC(fp_cos); break; case cTan: HANDLE_UNARY_CONST_FUNC(fp_tan); break; case cCeil: if(GetIntegerInfo(tree.GetParam(0)) == IsAlways) goto ReplaceTreeWithParam0; HANDLE_UNARY_CONST_FUNC(fp_ceil); break; case cTrunc: if(GetIntegerInfo(tree.GetParam(0)) == IsAlways) goto ReplaceTreeWithParam0; HANDLE_UNARY_CONST_FUNC(fp_trunc); break; case cFloor: if(GetIntegerInfo(tree.GetParam(0)) == IsAlways) goto ReplaceTreeWithParam0; HANDLE_UNARY_CONST_FUNC(fp_floor); break; case cInt: if(GetIntegerInfo(tree.GetParam(0)) == IsAlways) goto ReplaceTreeWithParam0; HANDLE_UNARY_CONST_FUNC(fp_int); break; case cCbrt: HANDLE_UNARY_CONST_FUNC(fp_cbrt); break; // converted into cPow x 0.33333 case cSqrt: HANDLE_UNARY_CONST_FUNC(fp_sqrt); break; // converted into cPow x 0.5 case cExp: HANDLE_UNARY_CONST_FUNC(fp_exp); break; // convered into cPow CONSTANT_E x case cLog2: HANDLE_UNARY_CONST_FUNC(fp_log2); break; case cLog10: HANDLE_UNARY_CONST_FUNC(fp_log10); break; case cLog2by: if(tree.GetParam(0).IsImmed() && tree.GetParam(1).IsImmed()) { tree.ReplaceWithImmed( fp_log2(tree.GetParam(0).GetImmed()) * tree.GetParam(1).GetImmed() ); goto do_return; } break; case cArg: HANDLE_UNARY_CONST_FUNC(fp_arg); break; case cConj: HANDLE_UNARY_CONST_FUNC(fp_conj); break; case cImag: HANDLE_UNARY_CONST_FUNC(fp_imag); break; case cReal: HANDLE_UNARY_CONST_FUNC(fp_real); break; case cPolar: if(tree.GetParam(0).IsImmed() && tree.GetParam(1).IsImmed()) { tree.ReplaceWithImmed( fp_polar(tree.GetParam(0).GetImmed(), tree.GetParam(1).GetImmed() ) ); goto do_return; } break; case cMod: /* Can more be done than this? */ if(tree.GetParam(0).IsImmed() && tree.GetParam(1).IsImmed()) { tree.ReplaceWithImmed( fp_mod(tree.GetParam(0).GetImmed(), tree.GetParam(1).GetImmed()) ); goto do_return; } break; case cAtan2: { /* Range based optimizations for (y,x): * If y is +0 and x <= -0, +pi is returned * If y is -0 and x <= -0, -pi is returned (assumed never happening) * If y is +0 and x >= +0, +0 is returned * If y is -0 and x >= +0, -0 is returned (assumed never happening) * If x is +-0 and y < 0, -pi/2 is returned * If x is +-0 and y > 0, +pi/2 is returned * Otherwise, perform constant folding when available * If we know x <> 0, convert into atan(y / x) * TODO: Figure out whether the above step is wise * It allows e.g. atan2(6*x, 3*y) -> atan(2*x/y) * when we know y != 0 */ range p0 = CalculateResultBoundaries( tree.GetParam(0) ); range p1 = CalculateResultBoundaries( tree.GetParam(1) ); if(tree.GetParam(0).IsImmed() && fp_equal(tree.GetParam(0).GetImmed(), Value_t(0))) // y == 0 { if(p1.max.known && (p1.max.val) < Value_t(0)) // y == 0 && x < 0 { tree.ReplaceWithImmed( fp_const_pi() ); goto do_return; } if(p1.min.known && p1.min.val >= Value_t(0.0)) // y == 0 && x >= 0.0 { tree.ReplaceWithImmed( Value_t(0) ); goto do_return; } } if(tree.GetParam(1).IsImmed() && fp_equal(tree.GetParam(1).GetImmed(), Value_t(0))) // x == 0 { if(p0.max.known && (p0.max.val) < Value_t(0)) // y < 0 && x == 0 { tree.ReplaceWithImmed( -fp_const_pihalf() ); goto do_return; } if(p0.min.known && p0.min.val > Value_t(0)) // y > 0 && x == 0 { tree.ReplaceWithImmed( fp_const_pihalf() ); goto do_return; } } if(tree.GetParam(0).IsImmed() && tree.GetParam(1).IsImmed()) { tree.ReplaceWithImmed( fp_atan2(tree.GetParam(0).GetImmed(), tree.GetParam(1).GetImmed()) ); goto do_return; } if((p1.min.known && p1.min.val > Value_t(0)) // p1 != 0.0 || (p1.max.known && (p1.max.val) < fp_const_negativezero())) // become atan(p0 / p1) { CodeTree pow_tree; pow_tree.SetOpcode(cPow); pow_tree.AddParamMove(tree.GetParam(1)); pow_tree.AddParam(CodeTreeImmed(Value_t(-1))); pow_tree.Rehash(); CodeTree div_tree; div_tree.SetOpcode(cMul); div_tree.AddParamMove(tree.GetParam(0)); div_tree.AddParamMove(pow_tree); div_tree.Rehash(); tree.SetOpcode(cAtan); tree.SetParamMove(0, div_tree); tree.DelParam(1); } break; } case cPow: { if(ConstantFolding_PowOperations(tree)) goto redo; break; } /* The following opcodes are processed by GenerateFrom() * within fpoptimizer_bytecode_to_codetree.cc and thus * they will never occur in the calling context for the * most of the parsing context. They may however occur * at the late phase, so we deal with them. */ case cDiv: // converted into cPow y -1 if(tree.GetParam(0).IsImmed() && tree.GetParam(1).IsImmed() && tree.GetParam(1).GetImmed() != Value_t(0.0)) { tree.ReplaceWithImmed( tree.GetParam(0).GetImmed() / tree.GetParam(1).GetImmed() ); goto do_return; } break; case cInv: // converted into cPow y -1 if(tree.GetParam(0).IsImmed() && tree.GetParam(0).GetImmed() != Value_t(0.0)) { tree.ReplaceWithImmed( Value_t(1) / tree.GetParam(0).GetImmed() ); goto do_return; } // Note: Could use (mulgroup)^immed optimization from cPow break; case cSub: // converted into cMul y -1 if(tree.GetParam(0).IsImmed() && tree.GetParam(1).IsImmed()) { tree.ReplaceWithImmed( tree.GetParam(0).GetImmed() - tree.GetParam(1).GetImmed() ); goto do_return; } break; case cNeg: // converted into cMul x -1 if(tree.GetParam(0).IsImmed()) { tree.ReplaceWithImmed( -tree.GetParam(0).GetImmed() ); goto do_return; } break; case cRad: // converted into cMul x CONSTANT_RD if(tree.GetParam(0).IsImmed()) { tree.ReplaceWithImmed( RadiansToDegrees( tree.GetParam(0).GetImmed() ) ); goto do_return; } break; case cDeg: // converted into cMul x CONSTANT_DR if(tree.GetParam(0).IsImmed()) { tree.ReplaceWithImmed( DegreesToRadians( tree.GetParam(0).GetImmed() ) ); goto do_return; } break; case cSqr: // converted into cMul x x if(tree.GetParam(0).IsImmed()) { tree.ReplaceWithImmed( tree.GetParam(0).GetImmed() * tree.GetParam(0).GetImmed() ); goto do_return; } break; case cExp2: // converted into cPow 2.0 x HANDLE_UNARY_CONST_FUNC(fp_exp2); break; case cRSqrt: // converted into cPow x -0.5 if(tree.GetParam(0).IsImmed()) { tree.ReplaceWithImmed( Value_t(1) / fp_sqrt(tree.GetParam(0).GetImmed()) ); goto do_return; } break; case cCot: // converted into cMul (cPow (cTan x) -1) if(tree.GetParam(0).IsImmed()) { Value_t tmp = fp_tan(tree.GetParam(0).GetImmed()); if(fp_nequal(tmp, Value_t(0))) { tree.ReplaceWithImmed( Value_t(1) / tmp ); goto do_return; } } break; case cSec: // converted into cMul (cPow (cCos x) -1) if(tree.GetParam(0).IsImmed()) { Value_t tmp = fp_cos(tree.GetParam(0).GetImmed()); if(fp_nequal(tmp, Value_t(0))) { tree.ReplaceWithImmed( Value_t(1) / tmp ); goto do_return; } } break; case cCsc: // converted into cMul (cPow (cSin x) -1) if(tree.GetParam(0).IsImmed()) { Value_t tmp = fp_sin(tree.GetParam(0).GetImmed()); if(fp_nequal(tmp, Value_t(0))) { tree.ReplaceWithImmed( Value_t(1) / tmp ); goto do_return; } } break; case cHypot: // converted into cSqrt(cAdd(cMul(x x), cMul(y y))) if(tree.GetParam(0).IsImmed() && tree.GetParam(1).IsImmed()) { tree.ReplaceWithImmed( fp_hypot(tree.GetParam(0).GetImmed(), tree.GetParam(1).GetImmed()) ); goto do_return; } break; /* Opcodes that do not occur in the tree for other reasons */ case cRDiv: // version of cDiv case cRSub: // version of cSub case cDup: case cFetch: case cPopNMov: case cSinCos: case cSinhCosh: case cNop: case cJump: break; /* Should never occur */ /* Opcodes that we can't do anything about */ case cPCall: case cFCall: break; } do_return:; #ifdef DEBUG_SUBSTITUTIONS std::cout << "[" << (&stackptr) << "]Done ConstantFolding, result: "; DumpTree(tree); std::cout << "\n"; DumpHashes(tree); #endif } } /* BEGIN_EXPLICIT_INSTANTATION */ #include "instantiate.hh" namespace FPoptimizer_CodeTree { #define FP_INSTANTIATE(type) \ template void ConstantFolding(CodeTree& ); FPOPTIMIZER_EXPLICITLY_INSTANTIATE(FP_INSTANTIATE) #undef FP_INSTANTIATE } /* END_EXPLICIT_INSTANTATION */ #endif fparserc++-4.5.2/fpoptimizer/constantfolding.hh000066400000000000000000000003421332731714600215500ustar00rootroot00000000000000#ifndef FPOptimizer_ConstantFoldingHH #define FPOptimizer_ConstantFoldingHH #include "codetree.hh" namespace FPoptimizer_CodeTree { template void ConstantFolding(CodeTree& tree); } #endif fparserc++-4.5.2/fpoptimizer/consts.hh000066400000000000000000000025731332731714600176750ustar00rootroot00000000000000#include "fparser.hh" #include "extrasrc/fpaux.hh" #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 #endif /* #define CONSTANT_L10B 0.3010299956639811952137 // log10(2) #define CONSTANT_L10BI 3.3219280948873623478703 // 1/log10(2) #define CONSTANT_LB10 CONSTANT_L10BI // log2(10) #define CONSTANT_LB10I CONSTANT_L10B // 1/log2(10) */ #define CONSTANT_POS_INF HUGE_VAL // positive infinity, from math.h #define CONSTANT_NEG_INF (-HUGE_VAL) // negative infinity namespace FUNCTIONPARSERTYPES { template inline Value_t fp_const_pihalf() // CONSTANT_PIHALF { return fp_const_pi() * Value_t(0.5); } template inline Value_t fp_const_twopi() // CONSTANT_TWOPI { Value_t result( fp_const_pi() ); result += result; return result; } template inline Value_t fp_const_twoe() // CONSTANT_2E { Value_t result( fp_const_e() ); result += result; return result; } template inline Value_t fp_const_twoeinv() // CONSTANT_2EI { Value_t result( fp_const_einv() ); result += result; return result; } template inline Value_t fp_const_negativezero() { return -Epsilon::value; } } fparserc++-4.5.2/fpoptimizer/cse.cc000066400000000000000000000406671332731714600171320ustar00rootroot00000000000000#include "bytecodesynth.hh" #include "codetree.hh" #ifdef FP_SUPPORT_OPTIMIZER using namespace FUNCTIONPARSERTYPES; //using namespace FPoptimizer_Grammar; //#define DEBUG_SUBSTITUTIONS_CSE namespace { using namespace FPoptimizer_CodeTree; class TreeCountItem { size_t n_occurrences; size_t n_as_cos_param; size_t n_as_sin_param; size_t n_as_tan_param; size_t n_as_cosh_param; size_t n_as_sinh_param; size_t n_as_tanh_param; public: TreeCountItem() : n_occurrences(0), n_as_cos_param(0), n_as_sin_param(0), n_as_tan_param(0), n_as_cosh_param(0), n_as_sinh_param(0), n_as_tanh_param(0) { } void AddFrom(OPCODE op) { n_occurrences += 1; if(op == cCos) ++n_as_cos_param; if(op == cSin) ++n_as_sin_param; if(op == cSec) ++n_as_cos_param; if(op == cCsc) ++n_as_sin_param; if(op == cTan) ++n_as_tan_param; if(op == cCot) ++n_as_tan_param; if(op == cSinh) ++n_as_sinh_param; if(op == cCosh) ++n_as_cosh_param; if(op == cTanh) ++n_as_tanh_param; } size_t GetCSEscore() const { //size_t n_sincos = std::min(n_as_cos_param, n_as_sin_param); size_t result = n_occurrences;// - n_sincos; return result; } /* Calculate whether a sincos() would be useful. * Return values: 0 = not useful * 1,2 = yes * 1 = the tree is always a sin/cos parameter, * so once a sincos() is synthesized, the * tree itself does not need to be synthesized */ int NeedsSinCos() const { bool always_sincostan = (n_occurrences == (n_as_cos_param + n_as_sin_param + n_as_tan_param)); if((n_as_tan_param && (n_as_sin_param || n_as_cos_param)) || (n_as_sin_param && n_as_cos_param)) { if(always_sincostan) return 1; return 2; } return 0; } /* Calculate whether a sinhcosh() would be useful. */ int NeedsSinhCosh() const { bool always_sincostan = (n_occurrences == (n_as_cosh_param + n_as_sinh_param + n_as_tanh_param)); if((n_as_tanh_param && (n_as_sinh_param || n_as_cosh_param)) || (n_as_sinh_param && n_as_cosh_param)) { if(always_sincostan) return 1; return 2; } return 0; } size_t MinimumDepth() const { size_t n_sincos = std::min(n_as_cos_param, n_as_sin_param); size_t n_sinhcosh = std::min(n_as_cosh_param, n_as_sinh_param); if(n_sincos == 0 && n_sinhcosh == 0) return 2; return 1; } }; template class TreeCountType: public std::multimap > > { }; template void FindTreeCounts( TreeCountType& TreeCounts, const CodeTree& tree, OPCODE parent_opcode, bool skip_root = false) { typename TreeCountType::iterator i = TreeCounts.lower_bound(tree.GetHash()); if(!skip_root) { bool found = false; for(; i != TreeCounts.end() && i->first == tree.GetHash(); ++i) { if(tree.IsIdenticalTo( i->second.second ) ) { i->second.first.AddFrom(parent_opcode); found = true; break; } } if(!found) { TreeCountItem count; count.AddFrom(parent_opcode); TreeCounts.insert(i, std::make_pair(tree.GetHash(), std::make_pair(count, tree))); } } for(size_t a=0; a BalanceResultType IfBalanceGood(const CodeTree& root, const CodeTree& child) { if(root.IsIdenticalTo(child)) { BalanceResultType result = {true,true}; return result; } BalanceResultType result = {true,false}; if(root.GetOpcode() == cIf || root.GetOpcode() == cAbsIf) { BalanceResultType cond = IfBalanceGood(root.GetParam(0), child); BalanceResultType branch1 = IfBalanceGood(root.GetParam(1), child); BalanceResultType branch2 = IfBalanceGood(root.GetParam(2), child); if(cond.FoundChild || branch1.FoundChild || branch2.FoundChild) { result.FoundChild = true; } // balance is good if: // branch1.found = branch2.found OR (cond.found AND cond.goodbalance) // AND cond.goodbalance OR (branch1.found AND branch2.found) // AND branch1.goodbalance OR (cond.found AND cond.goodbalance) // AND branch2.goodbalance OR (cond.found AND cond.goodbalance) result.BalanceGood = ( (branch1.FoundChild == branch2.FoundChild) || (cond.FoundChild && cond.BalanceGood) ) && (cond.BalanceGood || (branch1.FoundChild && branch2.FoundChild)) && (branch1.BalanceGood || (cond.FoundChild && cond.BalanceGood)) && (branch2.BalanceGood || (cond.FoundChild && cond.BalanceGood)); } else { bool has_bad_balance = false; bool has_good_balance_found = false; // Balance is bad if one of the children has bad balance // Unless one of the children has good balance & found for(size_t b=root.GetParamCount(), a=0; a bool ContainsOtherCandidates( const CodeTree& within, const CodeTree& tree, const FPoptimizer_ByteCode::ByteCodeSynth& synth, const TreeCountType& TreeCounts) { for(size_t b=tree.GetParamCount(), a=0; a& leaf = tree.GetParam(a); typename TreeCountType::iterator synth_it; for(typename TreeCountType::const_iterator i = TreeCounts.begin(); i != TreeCounts.end(); ++i) { if(i->first != leaf.GetHash()) continue; const TreeCountItem& occ = i->second.first; size_t score = occ.GetCSEscore(); const CodeTree& candidate = i->second.second; // It must not yet have been synthesized if(synth.Find(candidate)) continue; // And it must not be a simple expression // Because cImmed, VarBegin are faster than cFetch if(leaf.GetDepth() < occ.MinimumDepth()) continue; // It must always occur at least twice if(score < 2) continue; // And it must either appear on both sides // of a cIf, or neither if(IfBalanceGood(within, leaf).BalanceGood == false) continue; return true; } if(ContainsOtherCandidates(within, leaf, synth, TreeCounts)) return true; } return false; } template bool IsDescendantOf(const CodeTree& parent, const CodeTree& expr) { for(size_t a=0; a bool GoodMomentForCSE(const CodeTree& parent, const CodeTree& expr) { if(parent.GetOpcode() == cIf) return true; // Good if it's one of our direct children // Bad if it is a descendant of only one of our children for(size_t a=0; a size_t CodeTree::SynthCommonSubExpressions( FPoptimizer_ByteCode::ByteCodeSynth& synth) const { if(GetParamCount() == 0) return 0; // No subexpressions to synthesize. size_t stacktop_before = synth.GetStackTop(); /* Find common subtrees */ TreeCountType TreeCounts; FindTreeCounts(TreeCounts, *this, GetOpcode(), true); /* Synthesize some of the most common ones */ for(;;) { size_t best_score = 0; #ifdef DEBUG_SUBSTITUTIONS_CSE std::cout << "Finding a CSE candidate, root is:" << std::endl; DumpHashes(*this); #endif typename TreeCountType::iterator cs_it ( TreeCounts.end() ); for(typename TreeCountType::iterator j = TreeCounts.begin(); j != TreeCounts.end(); ) { typename TreeCountType::iterator i( j++ ); const TreeCountItem& occ = i->second.first; size_t score = occ.GetCSEscore(); const CodeTree& tree = i->second.second; #ifdef DEBUG_SUBSTITUTIONS_CSE std::cout << "Score " << score << ":\n" << std::flush; DumpTreeWithIndent(tree); #endif // It must not yet have been synthesized if(synth.Find(tree)) { TreeCounts.erase(i); continue; } // And it must not be a simple expression // Because cImmed, VarBegin are faster than cFetch if(tree.GetDepth() < occ.MinimumDepth()) { TreeCounts.erase(i); continue; } // It must always occur at least twice if(score < 2) { TreeCounts.erase(i); continue; } // And it must either appear on both sides // of a cIf, or neither if(IfBalanceGood(*this, tree).BalanceGood == false) { TreeCounts.erase(i); continue; } // It must not contain other candidates if(ContainsOtherCandidates(*this, tree, synth, TreeCounts)) { // Don't erase it; it may be a proper candidate later continue; } if(!GoodMomentForCSE(*this, tree)) { TreeCounts.erase(i); continue; } // Is a candidate. score *= tree.GetDepth(); if(score > best_score) { best_score = score; cs_it = i; } } if(best_score <= 0) { #ifdef DEBUG_SUBSTITUTIONS_CSE std::cout << "No more CSE candidates.\n" << std::flush; #endif break; // Didn't find anything. } //const TreeCountItem& occ = cs_it->second.first; const CodeTree& tree = cs_it->second.second; #ifdef DEBUG_SUBSTITUTIONS_CSE std::cout << "Found Common Subexpression:"; DumpTree(tree); std::cout << std::endl; #endif #if 0 int needs_sincos = occ.NeedsSinCos(); int needs_sinhcosh = occ.NeedsSinhCosh(); CodeTree sintree, costree, sinhtree, coshtree; if(needs_sincos) { sintree.AddParam(tree); sintree.SetOpcode(cSin); sintree.Rehash(); costree.AddParam(tree); costree.SetOpcode(cCos); costree.Rehash(); if(synth.Find(sintree) || synth.Find(costree)) { if(needs_sincos == 2) { // sin, cos already found, and we don't // actually need _this_ tree by itself TreeCounts.erase(cs_it); continue; } needs_sincos = 0; } } if(needs_sinhcosh) { sinhtree.AddParam(tree); sinhtree.SetOpcode(cSinh); sinhtree.Rehash(); coshtree.AddParam(tree); coshtree.SetOpcode(cCosh); coshtree.Rehash(); if(synth.Find(sinhtree) || synth.Find(coshtree)) { if(needs_sinhcosh == 2) { // sinh, cosh already found, and we don't // actually need _this_ tree by itself TreeCounts.erase(cs_it); continue; } needs_sinhcosh = 0; } } #endif /* Synthesize the selected tree */ tree.SynthesizeByteCode(synth, false); TreeCounts.erase(cs_it); #ifdef DEBUG_SUBSTITUTIONS_CSE synth.template Dump<0> (); std::cout << "Done with Common Subexpression:"; DumpTree(tree); std::cout << std::endl; #endif #if 0 if(needs_sincos) { if(needs_sincos == 2 || needs_sinhcosh) { // make a duplicate of the value, since it // is also needed in addition to the sin/cos. synth.FindAndDup(tree); } synth.AddOperation(cSinCos, 1, 2); synth.StackTopIs(sintree, 1); synth.StackTopIs(costree, 0); } if(needs_sinhcosh) { if(needs_sincos) synth.FindAndDup(tree); if(needs_sinhcosh == 2) { // make a duplicate of the value, since it // is also needed in addition to the sin/cos. synth.FindAndDup(tree); } synth.AddOperation(cSinhCosh, 1, 2); synth.StackTopIs(sinhtree, 1); synth.StackTopIs(coshtree, 0); } #endif } return synth.GetStackTop() - stacktop_before; } } /* BEGIN_EXPLICIT_INSTANTATION */ #include "instantiate.hh" namespace FPoptimizer_CodeTree { #define FP_INSTANTIATE(type) \ template \ size_t CodeTree::SynthCommonSubExpressions( \ FPoptimizer_ByteCode::ByteCodeSynth& synth) const; FPOPTIMIZER_EXPLICITLY_INSTANTIATE(FP_INSTANTIATE) #undef FP_INSTANTIATE } /* END_EXPLICIT_INSTANTATION */ #endif fparserc++-4.5.2/fpoptimizer/debug.cc000066400000000000000000000113331332731714600174320ustar00rootroot00000000000000#include "codetree.hh" #include "opcodename.hh" #ifdef FP_SUPPORT_OPTIMIZER #include #include #include #include #include using namespace FUNCTIONPARSERTYPES; #ifdef FUNCTIONPARSER_SUPPORT_DEBUGGING namespace { template void DumpHashesFrom( const FPoptimizer_CodeTree::CodeTree& tree, std::map >& done, std::ostream& o) { for(size_t a=0; a void DumpHashes(const CodeTree& tree, std::ostream& o) { std::map > done; DumpHashesFrom(tree, done, o); for(std::map >::const_iterator i = done.begin(); i != done.end(); ++i) { const std::set& flist = i->second; if(flist.size() != 1) o << "ERROR - HASH COLLISION?\n"; for(std::set::const_iterator j = flist.begin(); j != flist.end(); ++j) { o << '[' << std::hex << i->first.hash1 << ',' << i->first.hash2 << ']' << std::dec; o << ": " << *j << "\n"; } } } template void DumpTree(const CodeTree& tree, std::ostream& o) { //o << "/*" << tree.Depth << "*/"; const char* sep2 = " "; /* o << '[' << std::hex << tree.Hash.hash1 << ',' << tree.Hash.hash2 << ']' << std::dec; */ switch(tree.GetOpcode()) { case cImmed: o << tree.GetImmed(); /* o << "(" << std::hex << *(const uint_least64_t*)&tree.GetImmed() << std::dec << ")"; */ return; case VarBegin: o << "Var" << (tree.GetVar() - VarBegin); return; case cAdd: sep2 = " +"; break; case cMul: sep2 = " *"; break; case cAnd: sep2 = " &"; break; case cOr: sep2 = " |"; break; case cPow: sep2 = " ^"; break; default: sep2 = " "; o << FP_GetOpcodeName(tree.GetOpcode()); if(tree.GetOpcode() == cFCall || tree.GetOpcode() == cPCall) o << ':' << tree.GetFuncNo(); } o << '('; if(tree.GetParamCount() <= 1 && sep2[1]) o << (sep2+1) << ' '; for(size_t a=0; a 0) o << ' '; DumpTree(tree.GetParam(a), o); if(a+1 < tree.GetParamCount()) o << sep2; } o << ')'; } template void DumpTreeWithIndent(const CodeTree& tree, std::ostream& o, const std::string& indent) { o << '[' << std::hex << (void*)(&tree.GetParams()) << std::dec << ',' << tree.GetRefCount() << ']'; o << indent << '_'; switch(tree.GetOpcode()) { case cImmed: o << "cImmed " << tree.GetImmed(); o << '\n'; return; case VarBegin: o << "VarBegin " << (tree.GetVar() - VarBegin); o << '\n'; return; default: o << FP_GetOpcodeName(tree.GetOpcode()); if(tree.GetOpcode() == cFCall || tree.GetOpcode() == cPCall) o << ':' << tree.GetFuncNo(); o << '\n'; } for(size_t a=0; a& tree, std::ostream& o); \ template void DumpTree(const CodeTree& tree, std::ostream& o); \ template void DumpTreeWithIndent(const CodeTree& tree, std::ostream& o, const std::string& indent); FPOPTIMIZER_EXPLICITLY_INSTANTIATE(FP_INSTANTIATE) #undef FP_INSTANTIATE } /* END_EXPLICIT_INSTANTATION */ #endif fparserc++-4.5.2/fpoptimizer/explicit_instantations.txt000066400000000000000000000015311332731714600233740ustar00rootroot00000000000000Functions / Classes needing explicit instantations, per module: codetree: CodeTree<> readbytecode: CodeTree::GenerateFrom() makebytecode: CodeTree::SynthesizeByteCode() cse: CodeTree::SynthCommonSubExpressions() transformations: CodeTree::RecreateInversionsAndNegations() hash: CodeTree::Rehash() CodeTree::FixIncompleteHashes() CodeTreeData::Recalculate_Hash_NoRecursion() -- ^needed because of linker bug optimize: ApplyGrammars() optimize_debug: DumpMatch() optimize_match: TestParams() optimize_synth: SynthesizeRule() rangeestimation: CalculateResultBoundaries() constantfolding: ConstantFolding() bytecodesynth: SequenceOpcodes[] AssembleSequence() debug: DumpHashes() DumpTree() DumpTreeWithIndent() grammar: DumpParams() ParamSpec_Compare() grammar_data: ParamSpec_Extract() - NOTE: FROM .y FILE fparserc++-4.5.2/fpoptimizer/fpoptimizer.txt000066400000000000000000000346541332731714600211610ustar00rootroot00000000000000FILES: fpoptimizer.hh - C++ structures fpoptimizer.dat - Optimization operations PLAN: 1. Convert fpoptimizer.dat into code that initializes Grammar - either automatically or manually - avoid runtime parsing of .dat file - prefer static const structs over constructor code *DONE: See fpoptimizer_grammar_gen.y . Produces fpoptimizer_grammar.cc . 2. Augment the CodeTree mechanism the following ways: - Add mechanism that generates a hash value for the contents of the tree, for easy comparison of identical subtrees *DONE - Any time a CodeTree changes in any way that may affect the hash, update the hash value for this node and recursively to all parents (Note: Ensure that a CodeTree may only have one parent.) 2. Create code that matches a given CodeTree node to Grammar, and finds the rules that match (only one should match at most, but it can match a number of different ways, but that does not matter; only utilize the first way) - If a matching rule is found, replace either the matched params or the entire node as instructed in the rule. Ensure that the node gets its hash updated, along with its parents recursively. - The matching can be optimized by grouping the rules by the Opcode Note: The matching can get really complex and/or heavy in the cases where the same operand (or worse, the same set of operands) is expected to be found in two different subtrees. It may require some O(n!) algorithms. However, those rules can be safely skipped if the runtime sees that it would be too costly to check them. 3. Algorithm for matching trees: First, match all nodes in the entire tree. (Do this only once.) Any time a hash of a node is updated, match that node. (When the hashes are changed recursively for a node and all its parents, match all those affected nodes after the hashes have been regenerated.) Repeat until no substitutions are done. Overall, first apply this algorithm using the [ENTRY] rules only. Then, apply the rules with [INTERMEDIATE] rules only. Finally, apply the rules with [FINAL] rules only. 3.5. Algorithm for matching nodes: TODO 4a. Algorithm for handling bytecode: First, convert the bytecode into a CodeTree. Then run the matching algorithm. Last, convert the produced CodeTree back into bytecode. When generating the bytecode, additional optimizations can be performed: - Converting raise-to-integer-powers into sequences of cInv/cDup/cFetch/cMul using the powi algorithm already existing in fpoptimizer.cc (be wary of generating too long sequences, though) *DONE - Converting multiply-by-integers into sequences of cNeg/cDup/cFetch/cAdd using the powi algorithm already existing in fpoptimizer.cc (be wary of generating too long sequences, though) *DONE - Reordering cMul operands such that inverted operands don't come first, to avoid creating cInv unless necessary. When inverted operands come in non-head positions, use cDiv instead of cMul. *DONE - Reordering cAdd operands such that negated operands don't come first, to avoid creating cNeg unless necessary. When negated operands come in non-head positions, use cSub instead of cAdd. *DONE - When an identical subtree is synthesized into bytecode more than once in a row, use cDup for the subsequential occurrences. To that point, reorder any commutative operands so as to increase the chances of cDup being utilized. *TODO 4b. Optional algorithm if an SSA evaluator is supported (possibly only if a JIT backend exists): 1. First, convert the bytecode into a CodeTree. 2. Then run the matching algorithm. 3. Then convert the produced CodeTree into SSA format. SSA is a medium-level intermediate language (chapter 4.3 in [1], [2]), in which each instruction takes one of the following forms: target = command source1 source2 <...> - commands such as add, mul, pow, cos, max, less jump

  • " << std::fixed; } std::cout << title << ": "; if(printTimeAsInt) std::cout << int(result); else std::cout << result; std::cout << (gPrintHTML ? " µs. (" : " us. (") << beautify(int(1e6/result)) << " " << unit << "/s)\n"; } void TakeResult() { struct timeval end; gettimeofday(&end, 0); double begin_d = begin.tv_sec * 1e6 + begin.tv_usec; double end_d = end.tv_sec * 1e6 + end.tv_usec; double diff_d = (end_d - begin_d) / this->reset_threshold; if(iter == this->reset_threshold || diff_d < result) { result = diff_d; } begin = end; } private: unsigned nloops; unsigned iter; unsigned reset_threshold; struct timeval begin; double result; }; template int run() { Parser_t fp, fp2; typename Parser_t::value_type values[3] = { .25, .5, .75 }; for(unsigned i = 0; i < FunctionsAmount; ++i) { // Parse function // -------------- if(gPrintHTML) std::cout << "\n
    \n

    Function:\n\"" << funcData[i].funcStr << "\"" << std::endl; else std::cout << "\n--- Function:\n\"" << funcData[i].funcStr << "\"" << std::endl; int res = fp.Parse(funcData[i].funcStr, funcData[i].paramStr); if(res >= 0) { std::cout << "Col " << res << ": " << fp.ErrorMsg() << std::endl; return 1; } const unsigned ParseLoops = 2000000; const unsigned EvalLoops = 20000000; const unsigned OptimizationLoops = 20000; const unsigned FuncLoops = 50000000; Test tester; if(gPrintHTML) std::cout << "

    \n"; } return 0; } int main(int argc, char* argv[]) { enum ParserType { FP_D, FP_F, FP_LD }; ParserType parserType = FP_D; for(int i = 1; i < argc; ++i) { if(std::strcmp(argv[1], "-html") == 0) gPrintHTML = true; else if(std::strcmp(argv[1], "-f") == 0) parserType = FP_F; else if(std::strcmp(argv[1], "-ld") == 0) parserType = FP_LD; else if(std::strcmp(argv[i], "--help") == 0 || std::strcmp(argv[i], "-help") == 0 || std::strcmp(argv[i], "-h") == 0 || std::strcmp(argv[i], "/?") == 0) { std::cout << "FunctionParser speedtest " << kVersionNumber << "\n\nUsage: " << argv[0] << " [