		Creating plugins for Yacas

	    Introduction

Yacas supports dynamical loading of libraries ("plugins") at runtime. This allows to interface with other libraries or external code and support additional functionality. In a Yacas session, plugins are seen as additional Yacas functions that become available after a plugin has been loaded. Plugins may be loaded at any time during a Yacas session.

The {libltdl} library, part of the {libtool} package and distributed
together with Yacas, is used to load the plugins. This means that
plugins will only function on platforms supported by {libltdl}. This
includes Linux, Mac OS X, Solaris, and HP-UX.

Plugins currently have to be written in C++ and require certain include files from the Yacas source tree. A plugin may be compiled as a shared object after Yacas itself has been compiled and installed successfully.

	    An example plugin

Here is what we need to do to make a new plugin:

*	Decide on an interface that will be visible in Yacas. In this example, we will create a new Yacas function {Func1(x,y)}, where {x}, {y} are floating-point real numbers; {Func1(x,y)} should return a floating-point real number corresponding to $Sin(x/y)$. So, our "API" consists of a single function with the following C++ prototype:

	// FILE: func1.h
	double func1 (double x, double y);

*	Write a C++ implementation of this function:

	// FILE: func1.cc
	#include "stubs.h"	// required
	#include "func1.h"	// our exported API
	
	#include <math.h>
	// we need math.h for sin()
	double func1 (double x, double y) {
	  return sin(x/y);
	}

*	Write the "Yacas stub" for our API. This file is written in the Yacas language and describes the function(s) of our API. Yacas processes this file using the library package {cstubgen} and prepares a C++ file with some Yacas-compatible glue; this file will be the "C++ stub" which we do not have to write ourselves.

For our simple plugin, we shall only need a few lines of code in the Yacas stub:

	/* FILE: func1_api.stub */
	Use("cstubgen.rep/code.ys");
	
	/* Start generating a C++ stub file */
	StubApiCStart("Func1");
	
	/* Write some documentation */
	StubApiCRemark("This function computes
	  beautiful waves.");
	
	/* define a plugin-specific include file */
	StubApiCInclude("\"func1.h\"");
	
	/* Declare a plugin-specific function */
	StubApiCFunction("double","func1","Func1",
	  { {"double","x"},{"double","y"}});
	
	/* generate a C++ stub file
	  "func1_api.cc" */
	StubApiCFile("libfunc1","func1_api");

Another example of a Yacas stub is found in the file {plugins/example/barepluginapi.stub} of the source tree.

*	Process the "Yacas stub" and generate a C++ stub file {func1_api.cc}.

	yacas -pc func1_api.stub

*	Both of our C++ files, {func1_api.cc} and {func1.cc}, now need
to be compiled into a shared object. We shall assume that the Yacas
include files have been installed in {/usr/local/include/yacas/}, and
that the {libtool} script is in the path.

	libtool c++ -I/usr/local/include/yacas/
	  -I/usr/local/include/yacas/plat/linux32/
	  -c func1.cc

	libtool c++ -I/usr/local/include/yacas/
	  -I/usr/local/include/yacas/plat/linux32/
	  -c func1_api.cc

*	Next, we need to combine both object files in the actual
plugin.

	libtool c++ -module -avoid-version 
	  -no-undefined -rpath `pwd` -o libfunc1.la
	  func1.lo func1_api.lo

If compilation succeeds, the dynamic library file {libfunc1.la} is
created. This is our plugin; now it could be installed into the Yacas
plugin path (usually {/usr/local/lib/yacas/}) with

	libtool cp example.la /usr/local/lib/yacas/example.la

But we can also keep it in the working directory. 

*	Yacas can use the new plugin after we load it:

	In> DllLoad("libfunc1");
	Out> True;

If the plugin is not installed in the Yacas plugin path, we have to
specify the path to the plugin, eg. {DllLoad("./libfunc1")}.

*	Finally, we can use the new function:

	In> Func1(2,3);
	Out> 0.61837;
When we are finished using the function, we might unload the DLL:

	In> DllUnload("libfunc1");
	Out> True;


	    A dynamically generated plugin

Since Yacas can load plugins at runtime, why not have them generated also at runtime? Here is how we could dynamically create plugin functions to speed up numerical calculations.

Suppose we had a numerical function on real numbers, e.g.

	In> f(x,y):=Sin(2*x*Pi)+Sqrt(2)*Cos(y*Pi);
	Out> True;

We can generate some C++ code that calculates this function for given floating-point arguments (not for multiple-precision numbers):

	In> CForm(f(x,y));
	Out> "sin(2 * x * Pi) + sqrt(2)
	* cos(y * Pi)";
(Note that we would need to define {Pi} in the C++ file.)

Now it is clear that all the steps needed to compile, link, and load a plugin that implements {f(x,y)} can be performed automatically by a Yacas script. (You would need a C++ compiler on your system, of course.)

This is implemented in the function {MakeFunctionPlugin()} (see file {unix.ys} in the {addons/} subdirectory). To use it, we might first define a function such as {f(x,y)} above and then call

	In> MakeFunctionPlugin("fFast", f(x,y));
	Function fFast(x,y) loaded from
	  ./plugins.tmp/libfFast_plugin_cc.so
	Out> True;
	In> fFast(2,3);
	Out> -1.41421;

Now we can use the function {fFast(x,y)} which is implemented using an external plugin library. The function {MakeFunctionPlugin()} assumes that all arguments and return values of functions are real floating-point numbers. The plugin libraries it creates are always in the {plugins.tmp/} subdirectory of current working directory and are named like {libNNN_plugin_cc.so}.

If {MakeFunctionPlugin()} is called again to create a plugin function with the same name (but different body), the DLL will be unloaded and loaded as necessary.


		Embedding Yacas into a {c} or {c++} application

The current version of {Yacas} (1.0.54) has preliminary facilities
for embedding Yacas into your own programs, through linking with
a normal library.

The {Yacas} header files are installed in {/usr/local/share/yacas/include},
and the libraries in {/usr/local/share/yacas/lib}. The main entry library
is the {cyacas} library, which offers an interface which can be reached
through a normal {c} or {c++} program. The API it offers is the following:

	void yacas_init();
Initialize Yacas. This function has to be called before calling the
other functions. This function establishes a main evaluation environment
for Yacas expressions to be simplified in.
	
	void yacas_eval(char* expression);
Evaluate an expression. The result (or possible error)
can be obtained through the {yacas_error} and {yacas_result} functions,
if so desired.

	char* yacas_error();
Return a pointer to a string explaining the error
if an error occurred, or NULL otherwise.

	char* yacas_result();
Return a string representation of the result of
evaluating an expression. This function is only meaningful if
there was no error. In the case of an error, the return value
of {yacas_result} should be considered undefined.

	char* yacas_output();
Return pointer to output printed while evaluating an expression.

	void yacas_exit();
Clean up all things related to the main Yacas
evaluation environment

	void yacas_interrupt();
Interrupt a calculation.


The directory {embed/} contains some examples demonstrating calling
Yacas from your own programs. Use the {makefile.examples} makefile
to compile the examples (it is in a separate makefile because it is
not yet guaranteed to work on all platforms, so it might otherwise
break builds on some platforms).

The simplest "hello world"-type example is {example2}, which reads:

	#include <stdio.h>
	#include "cyacas.h"
	
	int main(int argc, char** argv)
	{
	  yacas_init();
	  yacas_eval("D(x)Sin(x)");
	  if (!yacas_error())
	    printf("%s\n",yacas_result());  
	  yacas_exit();
	  return 0;
	}

And yields on the command line:

	$ ./example2
	Cos(x);


The example {example1} allows you to pass expressions on the command 
line:

	$ ./example1 "D(x)Sin(x)" "Taylor(x,0,5)Sin(x)"
	Input> D(x)Sin(x)
	Output> Cos(x);
	Input> Taylor(x,0,5)Sin(x)
	Output> x-x^3/6+x^5/120;

The file {example3} shows a piece of code accepting an expression
in Lisp syntax, the result being printed to the console using {PrettyForm}.

The interface described above will probably not change very much in the future,
but the way to compile and link might. In an ideal world, there would be
one library {libcyacas.a} and one header file {cyacas.h}, which get
installed in a place where all programs can find it. Perhaps a dynamic
link library is even better.



