;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Embedding REDUCE into Pure ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ========================================= 1. Checkout the REDUCE code (trunk only)) ========================================= $ svn co \ https://reduce-algebra.svn.sourceforge.net/svnroot/reduce-algebra/trunk/ \ reduce-algebra Check: there should be a folder called "reduce-algebra". ========================================= 2. Creating the reduce image (reduce.img) ========================================= $ cd reduce-algebra/csl/new-embedded $ make $ cd reduce-image Check: there should be an executable "reduce" and a file "reduce.img". Tests: $ ./reduce -i reduce.img -v quit; ============================== 3. Creating the shared library ============================== $ cd ../procedural $ make Check: there should be an executable "reduce" and a file "reduce.img". Tests: $ ./reduce -i reduce.img -v quit; Open the "Makefile" into an editor and add the following code after the line containing "all: reduce". #------------------------------------------------------------------------------ dll: arith01.o arith02.o arith03.o arith04.o arith05.o \ arith06.o arith07.o arith08.o arith09.o arith10.o \ arith11.o arith12.o bytes.o char.o driver.o \ embedcsl.o cslmpi.o cslread.o eval1.o eval2.o eval3.o \ eval4.o fasl.o fns1.o fns2.o fns3.o fwin.o gc.o \ preserve.o print.o restart.o sysfwin.o termed.o \ stubs.o $(CC) $(CFLAGS) -shared arith01.o arith02.o arith03.o arith04.o arith05.o \ arith06.o arith07.o arith08.o arith09.o arith10.o \ arith11.o arith12.o bytes.o char.o driver.o \ embedcsl.o cslmpi.o cslread.o eval1.o eval2.o eval3.o \ eval4.o fasl.o fns1.o fns2.o fns3.o fwin.o gc.o \ preserve.o print.o restart.o sysfwin.o termed.o \ stubs.o -lm -o reduce.dll #------------------------------------------------------------------------------ then: $ make dll Check: there should be a file "reduce.dll" Now, reduce.dll and reduce.img is all we need of the reduce system. ------------------------------------------------------------------- ==================== 4. Interface to Pure ==================== - Create a folder "reduce" in the Pure library or somewhere it can be found by Pure. e.g. $ sudo mkdir /usr/local/lib/pure/reduce - Copy the files "reduce.img" and "reduce.dll" to the created folder. e.g. $ sudo cp reduce.dll /usr/local/lib/pure/reduce $ sudo cp reduce.img /usr/local/lib/pure/reduce (* the "reduce-algebra" folder isn't used anymore) - Insert the following code into a file named "reduce.pure" and place it into the Pure reduce folder created previously, e.g. /usr/local/lib/pure/reduce. //----------------------------------------------------------------------------- using "lib:reduce/reduce.dll" ; //typedef int character_reader(void); //typedef int character_writer(int); /* * When a handle on an expression is returned to the user this is the * type it has. The type should be treated as opaque, please. */ //typedef void *PROC_handle; /* * Before trying to do anything at all you must call cslstart. This will * allocate memory, load an initial heap image etc etc. The argc and argv * passed here are as per normal C startup. The key issue here is how * Reduce will find the file "reduce.img" that it needs to load. There are * two plausible ways you can achieve this. * (a) argv[0] should contain a (for choice) fully rooted path as in * /usr/local/bin/reduce * or c:\my-reduce\binaries\reduce.exe * In that case the name of the supposed execitable has ".img" tagged * onto it, so the image is expected to be at say /usr/local/bin/reduce.img * If the application name is the name of a symbolic link then the * image file is looked for in the dircetory that the link points to. * (b) You can put "-i" "/path/to/reduce.img" in two consecutive entries * in argv to give an explicit indication of where the image file * is to be found. This should override any attempt to look via * argv[0]. * Most users should not need to provide any further options, but options * are decoded just as for the ordinary version of the system. * * Any textual output generated during system-started is sent, character * by character, via the callback function. Eg pass an option "-v" in * argv and see a big banner. */ extern void cslstart(int argc, char **argv, int *wr); /* * At the end of a run please call cslfinish to close down everything * in a reasonably orderly manner and release memory. I should cautiously * note that the system will have some memory leaks so cslfinish will * not return EVERYTHING that has been allocated - if this worries you * please search for and correct the defects. Again the callback function * is used to process any output generated during close-down. */ extern int cslfinish(int *wr); /* * As a general-purpose escape it is possible to get a Lisp function * (with no arguments) called. This function does that re-binding * what would otherwise be terminal input and output to the two * callback functions. By writing your own custom Lisp function and then * calling it this way you get almost ultimate flexibility, if not * convenience! For used of the Reduce algebra system I hope that the * suite of more specialised functions listed later on will prove * easier to use. */ extern int execute_lisp_function(char *fname,int *r, int *w); /* * The next collection of functions provide for interaction with the * Reduce algebra system using a model based on a Reverse Polish * Calculator. You use RPN-style calls to build a fragment of parse * tree and can them as Reduce to "simplify" it. Having simplified it * you can ask for a simple prefix-form of the result to be generated and * returned, and there are functions for traversing that. */ /* * Example: * Task: differentiate (x+1)^2 with respect to x * Method: clear_stack(); * push_symbol("x"); * push_small_integer(1); * make_function_call("plus",2); function plus has 2 arguments. * push_small_integer(2); * make_function_call("expt",2); * push_symbol("x"); * make_function_call("df",2); "df" is for differentiate. * To use this you need to learn * the named Reduce uses for all * relevant operations. * simplify(); up until now the form built * has been just the prefix form * (expt (plus x 1) 2). * dup(); because save will pop the stack * save(1); save in "memory number 1". * make_printable(); the simplified form is in * a Reduce internal representation, * so this restores it to simple * prefix form. * p = get_value(); * now p holds a handle on the result, and it can be traversed * using functions atom(), first(), rest() and functions that extract the * name of a symbol or the value of an integer. You should assume that the * handle becomes invalid as soon as you call one of the other functions. * this is because they can all trigger garbage collection and that can * relocate data. * The functions used to build expressions all return zero on success or * an error-code otherwise. In the initial release the error-codes are * not documented other than via reading the source files. Furthermore the * consequence of a calculation seeking input or generating output is not * well sorted. */ /* * After having called cslstart() you can set the I/O callback functions * using this. If you set one or both to NULL this indicates use of * stdin/stdout as per usual rather than an callback, otherwise whenever * anybody wants to read or write they use these procedures. It is then * your responsibility to cope with whatever text gets exchanged! */ extern int PROC_set_callbacks(int *r, int *w); /* * Load a Reduce "package". */ extern int PROC_load_package(char *name); /* * Set of clear a Reduce switch. As on "on expandlogs;" * which you do via PROC_set_switch("expandlogs", 1); * Use 0 to switch something off and 1 to switch it on. */ extern int PROC_set_switch(char *name, int val); /* * Set level of garbage collector noise. This might often be a bit irrelevant, * but * 0 no messages at all * +1 messages whenever garbage collection happens * +2 messages whenever a module of code is loaded * +4 extra details in the garbage collector messages * Note that if an ALWAYS_NOISY option (probably set as a side effect * of the debugging command line option "-g") is in play then any * call here has +1 and +2 forced active. */ extern int PROC_gc_messages(int n); /* * stack = nil; */ extern int PROC_clear_stack(); /* * stack = name . stack; */ extern int PROC_push_symbol(char *name); /* * stack = the-string . stack; */ extern int PROC_push_string(char *data); /* * stack = n . stack; * Small integers may be up to 28-bits of (signed) data, while * big integers can be almost any size and are denoted here by strings. * Eg: PROC_push_small_integer(134217727); largest positive small num * PROC_push_small_integer(-134217728); extreme negative case * PROC_push_big_integer("-12345678901234567890"); */ extern int PROC_push_small_integer(int n); extern int PROC_push_big_integer(char *n); extern int PROC_push_floating(double n); /* * Takes n items from the top of the stack and uses them as arguments * for a function as specified by the name. Leaves the result on the * top of the stack. Arguments will have been pushed with arg1 pushed * first and the last argument pushed last. */ extern int PROC_make_function_call(char *name, int n); /* * Save whatever is on top of the stack in memory location n. At present * I provide 100 memory locations, whihc ar enumbered 0 to 99. */ extern int PROC_save(int n); /* * Push the contents of memory location n onto the stack. */ extern int PROC_load(int n); /* * Duplicate the top stack element. */ extern int PROC_dup(); /* * Discard the top stack element. */ extern int PROC_pop(); /* * The top item on the stack is replaced with what happens when Reduce * is asked to "simplify" or "evaluate" it. The result can then be stored * or combined with other items, but it will not in general be in a format * directly convenient for use by humans. */ extern int PROC_simplify(); /* * Replace the top item on the stack with a version of the same expression * in a reasonably simple prefix notation. * This representation is NOT intended for re-input to any calculation - * it is only intended for inspecial by the client code that is using Reduce * via this interface. To that end it may in the future return big integers * in a form where they have been converted to Lisp strings and may make * other transformations that would hurt attempts to re-use the expression. */ extern int PROC_make_printable(); /* * Return a handle to the top item on the stack, and pop the stack. This * will normally be called immediately after a call to PROC_make_printable. * the stack is popped because I view the "printable" version as unsuitable * for further use. */ extern void* PROC_get_value(); /* * The next few functions are predicates that may be applied to handles. * An "atom" is any non-composite form. A fixnum is a small integer, and * a symbol is a name. */ extern int PROC_atom(void* p); extern int PROC_null(void* p); extern int PROC_fixnum(void* p); extern int PROC_floatnum(void* p); extern int PROC_string(void* p); extern int PROC_symbol(void* p); /* * If something is not an atom it will be a list, and the following two * functions return the components of it. In general non-atomic items will * be structured as * (fname arg1 arg2 ...) * with "well understood" function names "plus", "difference", "minus", * "times", "quotient", "expt" being used to denote use of the main * arithmetic connectives. A Lisp fanatic would have named the following * two functions PROC_car and PROC_cdr! */ extern void* PROC_first(void* p); extern void* PROC_rest(void* p); /* * If something is an atom then these make it possible to extract details * of what it represents. In due course I may support floating point values * and big numbers, but release 1 of those code concentrates on the basics. */ extern int PROC_integer_value(void* p); extern double PROC_floating_value(void* p); extern char *PROC_symbol_name(void* p); extern char *PROC_string_data(void* p); /* * I also provide some calls that support a sort of ultimate cop-out in * that they maye it possible to call Lisp code directly rather than * just invoking the Reduce simplifier. They also allow one to get back a * raw Lisp result which will have had gensym-names solidified but which * is otherwise unaltered. Note that the way this is achieved means that * things will FAIL if the Lisp result were to be a cyclic structure! */ /* * Replace the top item on the stack with whatever is obtained by using * the Lisp EVAL operation on it. Note that this is not intended for * casual use - if there is any functionality that you need PLEASE ask * me to put in a cleaner abstraction to support it. */ extern int PROC_lisp_eval(); /* * Return a handle to the top item on the stack, and pop the stack. * The value here will be a RAW LISP structure and NOT at all necessarily * anything neat. */ extern void* PROC_get_raw_value(); //----------------------------------------------------------------------------- ===================== 5. Test the interface ===================== - Copy the following code after having checked/adjusted the path to the reduce.img (const redimg) into a Pure session or into a file test.pure ////////////////////////////////////////////////////////////////////////////// using reduce::reduce; const redimg = "/usr/local/lib/pure/reduce/reduce.img" ; cslstart 4 {"","-i",redimg,"-v"} NULL ; display p = [] if PROC_null p ; = PROC_symbol_name p if PROC_atom p && PROC_symbol p ; = PROC_integer_value p if PROC_atom p && PROC_fixnum p ; = display (PROC_first p):display (PROC_rest p) if ~PROC_atom p ; // Task: differentiate (x+1)^2 with respect to x PROC_clear_stack ; PROC_push_symbol "x" ; PROC_push_small_integer 1 ; PROC_make_function_call "plus" 2 ; PROC_push_small_integer 2 ; PROC_make_function_call "expt" 2 ; PROC_push_symbol "x" ; PROC_make_function_call "df" 2 ; PROC_simplify ; PROC_dup ; PROC_save 1 ; PROC_make_printable; // try this when commented -> *SQ form let p = PROC_get_value; display p ; /////////////////////////////////////////////////////////////////////////////// The result of "display p" should be ["plus",["times",2,"x"],2], i.e. the derivative of (x+1)^2. _______ Notes: On Windows using MinGW/msys one may create an import library if using another toolchain than gcc by adding -Wl,--out-implib,reduce_dll.a to the Makefile code above. - In msys the created executable is "reduce.exe" instead of "reduce". - Under Windows the path to the reduce.img/reduce.dll folder may be like const redimg = "C:/.../Pure/lib/reduce/reduce.img" (Don't use backslashes). Reduce is a free (BSD) open-source, general computer algebra system. It's fast [2], stable and comprehensive. [1] A.C. Hearn, http://reduce-algebra.sourceforge.net/ [2] R. Fateman, Comparing the speed of programs for sparse polynomial multiplication, ACM SIGSAM Bulletin 37 (1) (2003) 4; http://www.cs.berkeley.edu/~fateman/papers/fastmult.pdf