Formulc

A byte-compiler for mathematical functions

Version 2.22

by Harald Helfgott


Copying

The following short explanation of the Library General Public License is essentially the same as that in the TeXinfo 2.16 distribution.

Formulc is free; this means that everyone is free to use it and free to redistribute it on a free basis. Formulc is not in the public domain; it is copyrighted and there are restrictions on its distribution, but these restrictions are designed to permit everything that a good cooperating citizen would want to do. What is not allowed is to try to prevent others from further sharing any version of these programs that they might get from you.

Specifically, I want to make sure that you have the right to give away copies of Formulc, that you receive source code or else can get it if you want it, that you can change Formulc or use pieces of it in new free programs, and that you know you can do these things.

To make sure that everyone has such rights, I have to forbid you to deprive anyone else of these rights. For example, if you distribute copies of Formulc, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights.

Also, for my own protection, I must make certain that everyone finds out that there is no warranty for the programs that relate to Formulc. If these programs are modified by someone else and passed on, I want their recipients to know that what they have is not what I distributed, so that any problems introduced by others will not reflect on my reputation.

The precise conditions of the license for Formulc are found in the GNU Library General Public License accompanying it. Linking Formulc to non-free programs is allowed.

Overview

It is very frustrating that there is nothing in the standard C and C++ libraries for evaluating mathematical functions at running time. Crafting a small interpreter on one's own is not very difficult, but a standard set of routines would save much time to many people. There are already a few freely available C programs that parse mathematical expressions. Unfortunately, all such programs I have seen are too slow for numerically intensive work.

Formulc is a byte-compiler for mathematical functions. In other words, given a string representing a function, Formulc translates it into a postfix representation designed to minimize execution time. The Formulc routines use this representation instead of the original string whenever the function has to be evaluated at any point. Formulc's byte-compiler design makes for a far greater speed than that of a simple interpreter.

Filter

Routines

Type

make formulc

to obtain the object file formulc.o (as well as the stand-alone Formulc).

If you do not want to use the random number generator provided with Formulc, compile formumr.o with

make formumr.o

You should then link formumr.o with your random number generator and with your own program.

  1. Evaluation
    formu translate(char *source, char *args, int *length, int *error);
    
    Examples:
    formu f,g;
    int leng;
    int error;
    
    f = translate("x^2 + sin(3*x) + 0.1", "x", &leng, &error);
    
    f = translate("1E+5 * x/x \n", "x", &leng, &error);
    
    gets(gsource);
    g=translate(gsource, "xyz", &length, &error);
        /* g is a function of x, y and z */
    
    This function transforms a mathematical function in the usual representation (char *source) into a data structure containing the function in precompiled form (the return value, of type formu). The arguments of source (x,y,z,etc.) are in string arg[]. They must be lowercase letters. If any error occurs, translate will return empty code (see fnot_empty()), *length will have the value 0, and *error will be the index of the character in source[] that caused the error. (The user is strongly recommended to detect errors through *error instead of *length. *length is returned only for the sake of compatibility with older versions, and might be removed in the future.) The result string is dynamically allocated. The user can deallocate the space by calling destrf(). The syntax of source is as follows (in Extended Backus-Naur Form):
    source = expression "\0"
    expression = [-] summand {("+" | "-") summand}
    summand = factor {("*" | "/") factor}
       /*Please, don't forget "*" ! */
    factor = base {"^" base}
    base = "(" expression ")" | function "(" expression ")" |
            function "(" expression "," expression ")" |
    function "(" expression "," expression "," expression ")" |
                     number | parameter
    function = a string in the function table
    parameter = a lower-case letter mentioned by arg[]
    number = any nonnegative number  /* the sign is considered in
                                        the definition of expression */
                            /* please write 3.5E-3, not 3.5e-3 */
    
    This description of the syntax mimics the algorithm in translate(). Please notice that -3+5*6 and 3+5*(-6) are allowed, but 3+5*-6 and 3+-5*6 are not. Also notice that functions without parameters (such as pi()) must still be followed by parentheses. White space is ignored. Precedence:
    parentheses, function
    power
    unary minus
    * /
    + -
    
    If all operands are constants, translate() executes fval() to evaluate the expression at "compile time". (The inbuilt random-number generator is tagged so as not to be called at compile time. Other functions can also be thus tagged.) The standard functions are:
    exp()
    ln()
    sin()
    cos()
    tan()
    asin()
    acos()
    atan()
    atan2()
    abs()
    pi() (without parameters inside the parentheses)
    sqrt()
    rnd()
    
    One can add new functions by using fnew(). The names of the new functions must obey the same rules as names of C functions: the first character of a name must be a letter, and following characters can be either letters or numbers.

    double fval(formu function, char *args, ...);
    
    Example:
    function=translate("x^2+sin(cos(x))","x", &leng, &error);
    result=fval(f,"x",3.0);  /* 3.0, not 3 */
    /* returns 3^2+sin(cos(3)) */
    
    g=translate("n*m","nm", &leng, &error);
    for(p=0; p<100; p++)
     for(q=0; q<1000; q++)
      a[p][q]=fval(g,"nm",(double) p,(double) q);
    /* the arguments must always be double */
    
    fval() calculates the value of formu function when the parameters listed in char *args have the given values. If fval attempts any invalid arithmetical operations (division by zero, extracting a square root of a negative number), errno is EDOM (domain error) or ERANGE (range error) and/or the result is Infinity or Not-A-Number, depending on the platform.

    double f_x_val(formu function, double x);
    
    Example:
    result=f_x_val(f,3); /* calculates f(3) if f is a function of x */
    
    A shorthand version of fval() for functions on x. It is about as fast as fval().

    double fval_at(formu function);
    void make_var(char var, double value);
    
    This is the interface for the evaluation of functions whose number of parameters is determined at run-time. Example:
    make_var('x',2);
    make_var('y',0.2);
    make_var('z',-2);
    result=fval_at(f);
    /* evaluates f(x,y,z) at point x=2, y=0.2, z=-2 */
    
  2. Management of the Function Library
    int fnew(char *name, Func f, int n_pars, int varying);
    
    Examples:
    fnew("sinh",(Func) sinh,1,0);  /* sinh has been declared as
                                      double sinh(double); */
    fnew("logb",(Func) logb,2,0);  /* logb has been declared as
                                      double logb(double, double); */
    fnew("rnd", (Func) rnd, 0,1);  /* varying = 1 because the result
                                      of rnd is not determined
                                      by its parameters. */
    
    fnew() adds a user-defined C function with double parameters to the library of Formulc. The C function can have from 0 to 3 parameters. fnew() returns 0 if the library is full (255 functions) or if some other error occurs. Otherwise, it returns 1. Do not call any function 'E'.

    int read_table(int i, char *name, int *n_of_pars, int *varying);
    
    Example:
    i=0;
    while(read_table(i++, name, &n_pars, &varying)
        printf("%s %d",name,n_pars);
    
    read_table() copies the name, the number of parameters and the non-determinism bit of function #i onto name[], *n_of_pars and *varying. If function #i does not exist, read_table() returns 0. Otherwise, it returns a positive number.

    int where_table(char *name);
    
    Example:
    if(where_table("sinh") == -1)
     printf("Hyperbolic sine not defined.");
    
    If there is a mathematical function called name[], where_table() returns its index in the table of functions. Otherwise where_table() returns -1. Use fnew to register functions which are not in Formulc's standard library.

    int fdel(char *name);
    
    Example:
    if(where_table("sinh" != -1)
     fdel("sinh");
    
    If there is a mathematical function called name[], fdel() deletes it. If fdel() is successful, it returns a non-negative number. Otherwise, it returns -1.
  3. Random-Number Generation The standard library of Formulc includes double rnd(void). Any program that generates random numbers through Formulc must call
    void rnd_init(void);
    
    first. By default, Formulc uses the random-number generator r250() (written by W. L. Maier, S. Kirkpatrick and E. Stoll). If you want to use another RNG, you must define functions according to the declarations
    double rnd(void);
    void rnd_init(void);
    
    Furthermore, you must compile formulc with
    make myrnd
    
    as stated at the beginning of this section.
  4. Miscellaneous
    void destrf(formu f);
    
    frees memory occupied by f.
    void make_empty(formu f);
    
    makes f an empty function. Note that this function should not to be used as a destructor, as it does not deallocate any memory. You should use destrf() for this purpose.
    int fnot_empty(formu f);
    
    returns 1 if f is not an empty function; returns 0 otherwise.
    const char *fget_error(void);
    
    Every routine in Formulc gives an error message if it fails and erases the previous error message from memory if it succeeds. fget_error() renders an error string if the last routine failed; otherwise, it returns NULL. The only routine that does not alter the error message is fget_error() itself. Usually, error messages are self-explanatory. If you ever see an error message that begins with I1, I2 or I3, please send tell me (hhelf@cs.brandeis.edu). You should never see such a thing.

History

On May 1993, a mathematics professor in Lima, Peru asked me whether I could write a fast formula interpreter. His program needed to compute functions entered by the user hundreds of thousands of times. I decided, therefore, to write a two-stage interpreter. A two-stage interpreter is at a middle point between a compiler and an interpreter: it is system-independent and easily linkable, just like a good interpreter, but the speed of the code it produces is close to the speed of compiled code.

The first version of Formulc to be distributed through the INTERNET was v2.1. Formulc v2.1 could not run in some computers, and lacked a UNIX-filter front end. Thanks to the feedback of several users, I was able to eliminate these two defects.

The main difference between Formulc 2.2 and Formulc 2.22 is that the newer version can be distributed freely under the GNU Library General Public License. No changes have been made to the UNIX front-end or to the behaviour of the C routines.

I have been considering changing both the Formulc front-end and the declarations of the Formulc routines so as to make them more SCHEME-like. In particular, I would like to make the names of the variables be declared only at byte-compilation time, so as to These changes would allow me to make both the code and the interface more elegant. On the other hand, they would obligue all those who have used Formulc until now to change their code. There would be no increase or decrease in efficiency. Please send me e-mail with your opinion, if you have one. My address as of February 1998 is:

Harald Helfgott
MB 1807 Brandeis University
Waltham, MA 02254-9110
U.S.A.
hhelf@cs.brandeis.edu

Acknowledgements

I thank all users who have proposed, or actually implemented, improvements to Formulc. I am especially grateful to Ralf Groesse-Kunstleve (rwgk@laplace.csb.yale.edu) for having written the command-line front end.

Concept Index

a

  • Acknowledgements
  • address
  • b

  • Byte compiler
  • c

  • C Functions, Formulc as
  • Changes from previous versions of Formulc
  • Compilation
  • Copying
  • Copyright
  • d

  • destrf
  • Distribution
  • e

  • Evaluation
  • f

  • f_x_val
  • fdel
  • Filter
  • fnew
  • fnot_empty
  • Formulc 2.2
  • Formulc as C functions
  • Free
  • Free software
  • Function Library, Management of
  • Function Package, Formulc as
  • Further development
  • Future plans
  • fval
  • g

  • General Public License
  • GPL
  • h

  • History
  • i

  • Interpreter
  • Introduction
  • l

  • License
  • m

  • make_empty
  • o

  • Overview
  • r

  • Random-number Generation
  • read_table
  • Right
  • Routines
  • s

  • Stand-alone Formulc
  • t

  • Thanks
  • u

  • UNIX Filter
  • w

  • Warranty
  • where_table

  • This document was generated on 23 Febuary 1998 using the texi2html translator version 1.51.