
/*
Plotting a one-dimensional function: an example using threading

Most programming languages in use are imperial, and thus force the
programmer to write out an algorithm in detail, step by step
instructions. The interpreter language in Yacas is one derived
from Lisp, and thus has many functional properties. Functional
descriptions of algorithms are usually a lot shorter.

This example shows the use of threading, to write a function plotter
in a few lines. It also shows how to build up a nice program from
small functions.

Threading can be used to evaluate a function at multiple values:
	x:={a,b,c};
	Sin(x);

should return something to the effect of

	{Sin(a),Sin(b),Sin(c)}


This can be useful for plotting functions. For instance,

	(0 .. 5)/5


evaluates to

	{0,1/5,2/5,3/5,4/5,1};

And

	N((0 .. 5)/5)

evaluates to

	{0,0.2,0.4,0.6,0.8,1};

So this in effect generates a list of values from 0 to 1 in 5 steps.
So entering

	a + (0 .. 5)/5

will result in

	{a,a+1/5,a+2/5,a+3/5,a+4/5,a+1};

which is a list from a to (a+1).

A list of values we want to evaluate the
function at can then be built like this:

*/
	Iterate(start_IsNumber, end_IsNumber, steps_IsInteger) <--
		start + ( (0 .. steps)/steps) * (end-start);

/*
This function uses the
threading facilities, where addition, multiplication and division
on lists work on a per-element basis.

Save the function 'Iterate' in a file, start up yacas, and load the file
using Load("file"), and then type

	Iterate(0,1,4);

This should return {0,1/4,1/2,3/4,1}

Now, type

	x:=Iterate(0,1,4);
	N(Sin(x));

This should give you a list of Sin(x) evaluated at the four points.
Again N(...) should be used, to force evaluation of Sin on a number.
Now, if you type

    Transpose({x,N(Sin(x))});

You will get a list of the form

	{
	  {x0, Sin(x0)},
	  {x1, Sin(x1)},
	  {x2, Sin(x2)},
	  {x3, Sin(x3)}
	}

We need to be able to write this out as something a plotting library
like gnuplot accepts. Suppose we create a function
*/

	WriteTuple(tuple_IsList) <--
	  [
		ForEach(item,tuple)
		[
		  Write(item);
		  Space();
		];
		NewLine();
	  ];

/*
We could then write out each item in the list:

	list:=Transpose(x,N(Sin(x)));
	ForEach(item,list) WriteTuple(item);


To store this to a file, ToFile can be used:

	ToFile("data.dat") ForEach(item,list) WriteTuple(item);

Plotting in gnuplot can then be achieved by using "plot 'data.dat' with lines" in
gnuplot.

So the full sequence for plotting Sin(x) with x from -3 to 3 in 10 steps
would become:

    Load("myplot.ys");
	x:=Iterate(-3,3,10);
	list:=Transpose({x,N(Sin(x))});
	ToFile("data.dat") ForEach(item,list) WriteTuple(item);

Where the file "myplot" should contain the functions Iterate and WriteTuple.

*/
/*
PlotX(from_IsNumber,to_IsNumber,steps_IsInteger,_function) <--
[
  Local(x,list);
  x:=N(Iterate(from,to,steps));
  function:=N(function);
  If(IsList(function[1]),function:=function[1]);
  list:=Transpose({x,function});
  ForEach(item,list) WriteTuple(item);
];
*/
/* HoldArg("PlotX",arg4); */

PlotXY(from_IsNumber,to_IsNumber,steps_IsInteger,_function) <--
[
	Local(var, x, item, list, result);
	var :=VarList(function);	// variable name
	If(Length(var) != 1,
		Echo({"PlotXY: Error: expression is not a function of one variable:":Nl(), function}),
		[
			x := N(Iterate(from,to,steps));	// list of abscissa points
			list := Transpose ({x, N(MapSingle({var, function}, x))} );
			ForEach(item, list)	// putting Transpose inside ForEach didn't work b/c of reference to temporary, most probably
				WriteTuple(item);
		]
	);
];

10 # GnuPlot(from_IsNumber,to_IsNumber,steps_IsInteger,list_IsList) <--
[
  Local(i,exec, dirBase);
  exec := "plot ";
  dirBase := "gnuplot.tmp/";
  // create directory if it does not exist
  SystemCall("test -d " : dirBase : "|| mkdir -p " : dirBase);
  For(i:=1,i<=Length(list),i++)
  [
    ToFile(dirBase:"gnudata.in":String(i)) PlotXY(from,to,steps,list[i]);
    exec:=exec:"\"":dirBase:"gnudata.in":String(i):"\" with lines";
    If(i<Length(list),exec:=exec:", ");
	Echo( "GnuPlot: created file ":dirBase:"gnudata.in":String(i) );
  ];
  ToFile(dirBase:"gnuplot.in") WriteString(exec);
  SystemCall("gnuplot -persist " : dirBase : "gnuplot.in");
];

20 # GnuPlot(from_IsNumber,to_IsNumber,steps_IsInteger,_function) <--
[
  Local(dirBase);
  dirBase := "gnuplot.tmp/";
  // create directory if it does not exist
  SystemCall("test -d " : dirBase : "|| mkdir -p " : dirBase);
  ToFile(dirBase:"gnudata.in") PlotXY(from,to,steps,function);
  ToFile(dirBase:"gnuplot.in") WriteString("plot ":"\"":dirBase:"gnudata.in\" with lines");
  Echo( "GnuPlot: created file ":dirBase:"gnudata.in" );
  SystemCall("gnuplot -persist ":dirBase:"gnuplot.in");
];
HoldArg("GnuPlot",arg4);


