Euler has a mighty programming language. But this language is restricted to numerical programming in the context of a matrix language. Like Matlab, it is the wrong choice as a general programming language.
To be able to add more general algorithms to Euler, Euler can load functions from a DLL (dynamic link library). These libraries can be programmed in any language, which can link to a DLL. E.g., Peter Notebaert compiled the LPSOLVE program for Euler to a DLL.
Of course, this section is for experienced programmers only.
To make it easy for programmers to add C code to Euler, Euler includes the Tiny C Compiler (TCC) by Fabrice Bellard. This compiler does some optimizations, and is usually well in the range of highly optimizing compilers. It is very small, and has support for the Windows API.
The syntax is an extended C. E.g., you can define variable at any place, but not in for loops internally. The integer size is 32 bit. See below for the necessary stuff to interface with Euler.
You can compile one C file to a DLL with this compiler right from the Euler notebook. Euler calls TCC with the necessary command line. The command is
>tccompile "filename";
You must not add the .c extension. The file must be in the current directory (the directory of the notebook), and the DLL will be saved in that directory. Compiler errors will print to the notebook window. TCC has a reasonable way to print errors.
If you press F10 at this line, the external editor will open with the C file loaded, if it is in the current directory. The Java editor je that comes with Euler has syntax highlighting for C.
For a first test,
Your directory should contain a DLL now. To see the code, press F10 in the line.
You can compile the DLL yourself in a command line without using the tccompile command, or create a batch file, if you like. The command you need is the following
?E\tcc\tcc -shared -I ?E\dll -o ?F.dll ?F.c ?E\dll\dlldef.c
Here, ?E must be replaced by the installation directory of Euler, and ?F must be replaced by the file name. As you see, tcc needs to include the dll directory in Euler for the headers with -I, and it needs to include the compilation of dlldef.c in the same directory.
If you like you can enter a command inside the external editor to compile with one click. For this create a new program with the name TCC. Enter the command above with %N (will be replaced by the file name) for ?F, and the proper path for ?E. Mark the switch to see the output and insert the new command. You can now compile with Escape-1 or the "1" icon.
Using regular expressions, you can also make the editor got to the error line with one click. Enter
\:([0-9]*)\:
into the line for the regular expression, and (0) into the "line" field. Then you can double click on the error message to go the the line.
You need to obey certain rules in your code to extract the parameters from the Euler headers, and to return the results in a proper way.
Euler uses a stack on the heap to keep its data. You need to deliver your results in Euler stack elements on the stack. To make this easier, there are macros and functions in dlldef.c and dlldef.h. Both files are in the DLL directory of your Euler installation. You need to include dlldef.h and stdio.h. The file dlldef.h is well documented and explains how to create the headers of stack elements and access their data part.
Here is an example, which takes an integer and compute the Fibonacci numbers.
#include "dlldef.h" #include <stdio.h> EXPORT char * fib (header *hd[], int np, char *ramstart, char *ramend) { start(ramstart,ramend); // required !!! int n=(int)getreal(hd[0]); if (n<2) n=2; header *result=new_matrix(1,n); CHECK(result,"Stack overflow!"); double *m=matrixof(result); m[0]=1; m[1]=1; int i; for (i=2; i<n; i++) m[i]=m[i-1]+m[i-2]; return newram; // required !!! }
The function start is absolutely necessary. It sets the global variables ram, newram and ramend for other functions in the API.
The parameters of Euler are passed in a vector of parameters. The number of parameters is in np. (It is possible to write functions with a variable number of parameters.) We extract the real from the stack element with getreal. You may wish to study dlldef.c for details on this.
After minimal error checking we create a matrix of size n (1 row, n columns), and fill it up with the Fibonacci numbers.
To return the result, we return the value newram. This variable is increased by any new_... command. Euler assumes that the results start from the old value of newram. Note, that you can return multiple results just like in Euler functions. To return nothing, return 0 from main. To return an error message, copy it to char *ram, and return this value. The variable ram keeps the old stack position.
sprintf(ram,"Error in variable %s",variablename); return ram;
To load the function into Euler, use the DLL command. It takes the name if the library, without the extension .dll, the name of the function, and the number of parameters (-1 for a flexible number of parameters). The library is searched in the Euler path.
>closedll("test"); >tccompile test; >dll("test","fib",1); >fib(40) [ 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155 ] >fib(I) Need a real number of fib!
Note that you need to reload the Euler notebook to recompile the example, or use the closedll command as in the example. Otherwise the library will be locked by Euler.
You can use the stack as a working space. In this case, use the getram function. If you do this, newram will be increased, which would confuse Euler, since it assumes the result there. So you need to move the result to newram with moveresults. See the this example.
Of course, you can also malloc memory. Don't forget to free the memory later. This is useful to hold data in other formats than Euler provides. Of course, memory will be releases when Euler quits.
It is not easy to write proper C code, unless for very simple functions. Moreover, the C code has to use the Euler API properly. Here are the most important errors you can make.
Not calling the start routine. The start routine initializes the ram pointers, and resets the error. Not calling it crashes the DLL.
No error checking for Euler parameters. Euler only knows the name of the functions and the number of parameters. You need to check the type of the parameters properly. Use hd->type and the provided header types for this. Also check for proper array dimensions.
Returning the false value. Euler assumes that the results are at the pointer ramstart, and the end of the results is returned by the function. If there is no result, 0 must be returned. If the functions returns ramstart, Euler assumes an error string at ramstart, which it prints. If you need working space from the stack, either use the stack space after your result (check for ramend!), or move the result later to the ramstart with moveresults().
Not releasing memory. Your program will get slower and slower due to swapping if you do this. Any memory allocated with malloc must be releases with free. Note that releasing released memory crashes programs. Of course you can keep memory between function calls. But if you do not longer need it, release it. You can export a clear() function to release all memory at the end. It will be called automatically.
An example for an external DLL is the package, which contains functions to generate and keep global variables and lists.
Lists are not supported well in Euler. Growing vectors can be used, but can only store numbers or vectors. The list package contains functions to generate named lists.
Also, the type or size of global variables cannot be changed in Euler within functions. The package contains functions to hold global variables in a more flexible way.
This package should be used only if necessary.