<commit_before>		Designing modules in the Yacas scripting language

	    Introduction

For any software project where the source code grows to
a substantial amount of different modules, there needs to be
a way to define interfaces between the modules, and a way
to make sure the modules don't interact with the environment
in an unintended way. 

One hallmark of a mature programming language is that it 
supports modules, and a way to define its interface while
hiding the internals of the module. This section describes
the mechanisms for doing so in the Yacas scripting language.

	    Demonstration of the problem
 
Unintentional interactions between two modules typically happen 
when the two modules accidentally share a common "global"
resource, and there should be a mechanism to guarantee that this
will not happen.

The following piece of code is a little example that demonstrates
the problem:

	SetExpand(fn_IsString) <-- [expand:=fn;];
	ram(x_IsList)_(expand != "") <-- ramlocal(x);
	expand:="";
	ramlocal(x) := Map(expand,{x});

This little bit of code defines a function {ram} that calls the
function {Map}, passing the argument passed if it is a string, and
if the function to be mapped was set with the {SetExpand} function.
It contains the following flaws:

*	0. {expand} is a global variable with a rather generic name, one
that another module might decide to use.
*	0. {ramlocal} was intended to be used from within this module only, and
doesn't check for correctness of arguments (a small speed up optimization
that can be used for routines that get called often). As it is, it can be
called from other parts, or even the command line.
*	0. the function {ramlocal} has one parameter, named {x}, which is also
generic (and might be used in the expression passed in to the function),
and {ramlocal} calls {Map}, which calls {Eval} on the arguments. 

The above code can be entered into a file and loaded from the command
line at leisure. Now, consider the following command line interaction
after loading the file with the above code in it:

	In> ramlocal(a)         
	In function "Length" : 
	bad argument number 1 (counting from 1)
	Argument matrix[1] evaluated to a
	In function call  Length(a)
	CommandLine(1) : Argument is not a list

We called {ramlocal} here, which should not have been allowed.

	In> ram(a)
	Out> ram(a);

The function {ram} checks that the correct arguments are passed in
and that {SetExpand} was called, so it will not evaluate if these
requirements are not met. 

Here are some lines showing the functionality of this code as
it was intended to be used:

	In> SetExpand("Sin")
	Out> "Sin";
	In> ram({1,2,3})
	Out> {Sin(1),Sin(2),Sin(3)};

The following piece of code forces the functionality to break
by passing in an expression containing the variable {x}, which
is also used as a parameter name to {ramlocal}.

	In> ram({a,b,c})
	Out> {Sin(a),Sin(b),Sin(c)};
	In> ram({x,y,z})
	Out> {{Sin(x),Sin(y),Sin(z)},Sin(y),Sin(z)};

This result is obviously wrong, comparing it to the call above.
The following shows that the global variable {expand} is exposed
to its environment:

	In> expand
	Out> "Sin";


	    Declaring resources to be local to the module

The solution to the problem is {LocalSymbols}, which changes every
symbol with a specified name to a unique name that could never
be entered by the user on the command line and guarantees that it
can never interact with the rest of the system. The following code
snippet is the same as the above, with the correct use of {LocalSymbols}:


	LocalSymbols(x,expand,ramlocal) [
	  SetExpand(fn_IsString) <-- [expand:=fn;];
	  ram(x_IsList)_(expand != "") <-- ramlocal(x);
	  expand:="";
	  ramlocal(x) := Map(expand,{x});
	];


This version of the same code declares the symbols {x}, {expand}
and {ramlocal} to be local to this module.

With this the interaction becomes a little bit more predictable:

	In> ramlocal(a)
	Out> ramlocal(a);
	In> ram(a)
	Out> ram(a);
	In> SetExpand("Sin")
	Out> "Sin";
	In> ram({1,2,3})
	Out> {Sin(1),Sin(2),Sin(3)};
	In> ram({a,b,c})
	Out> {Sin(a),Sin(b),Sin(c)};
	In> ram({x,y,z})
	Out> {Sin(x),Sin(y),Sin(z)};
	In> expand
	Out> expand;


	    When to use and when not to use {LocalSymbols}

The {LocalSymbols} should ideally be used for every global variable,
for functions that can only be useful within the module and thus
should not be used by other parts of the system, 
and for local variables that run the risk of being passed into 
functions like {Eval}, {Apply}, {Map}, etcetera (functions that
re-evaluate expressions).

A rigorous solution to this is to make all parameters to functions
and global variables local symbols by default, but this might cause
problems when this is not required, or even wanted, behaviour.

The system will never be able to second-guess which function
calls can be exposed to the outside world, and which ones should
stay local to the system. It also goes against a design rule of Yacas:
everything is possible, but not obligatory. This is important 
at moments when functionality is not wanted, as it can be hard 
to disable functionality when the system does it automatically.

There are more caveats: if a local variable is made unique with 
{LocalSymbols}, other routines can not reach it by using the 
{UnFence} construct. This means that {LocalSymbols} is not always 
wanted. 

Also, the entire expression on which the {LocalSymbols} command works
is copied and modified before being evaluated, making loading
time a little slower. This is not a big problem, because the 
speed hit is usually during calculations, not during loading, but
it is best to keep this in mind and keep the code passed to 
{LocalSymbols} concise.








		How Yacas Deals With Sets of Solutions

	    Introduction

*REM @@@@@@@@@@@@@@@@@@@@@@@@
THIS IS A DRAFT, THE FUNCTIONALITY DESCRIBED BELOW IS NOT FULLY
IMPLEMENTED YET!!! THIS IS JUST A PROPOSAL FOR HOW SOLVE MIGHT
WORK.

*HEAD (This is a draft)

Worries: 

*	need to change all code that uses Solve
*	need a lot of changes in documentation
*	arguments to solve are a bit more verbose than the previous
version: Solve({eq1,eq2},vars) versus Solve(eq1 And eq2,vars). Suddenly
things like Solve(leftlist==rightlist,vars) is not possible any more.
This has to be done with extra commands (which is ok?). It can not
stay the way it was, because lists now mean something else, a collection
of disjunct solutions.

*REM @@@@@@@@@@@@@@@@@@@@@@@@

The difference between a problem stated and a solution given is
a subtle one. From a mathematical standpoint, 

	In> Integrate(x,0,B)Cos(x)
	Out> Sin(B);

And thus

	Integrate(x,0,B)Cos(x) == Sin(B)

is a true statement. Furthermore, the left hand side is mathematically
equivalent to the right hand side. Working out the integration, to
arrive at an expression that doesn't imply integration any more is
generally perceived to be a more desirable result, even though
the two sides are equivalent mathematically.

This implies that the statement of a set of equations declaring
equalities is on a same footing as the resulting equations stating
a solution: 

$$ a*x+b==c => x==(c-b)/a $$. 

If the value of x is needed, the right hand side is more desirable.

Viewed in this way, the responsibility of a {Solve} function could
be to manipulate a set of equations in such a way that a certain
piece of information can be pried from it (in this case the value
of $ x == x(a,b,c) $.

A next step is to be able to use the result returned by a {Solve}
operation. 

	    Implementation Semantics of Solve in Yacas

Suppose there is a set of variables that has a specific combination
of solutions and these solutions need to be filled in in an expression:
the {Where} operator can be used for this:

	In> x^2+y^2 Where x==2 And y==3
	Out> 13;

Solve can return one such solution tuple, or a list of tuples.
The list of equations can be passed in to Solve in exactly the same
way. Thus:

	In> Solve(eq1,var)
	Out> a1==b1;
	In> Solve(eq1 And eq2 And eq3,varlist)
	Out> {a1==b1 And a2==b2,a1==b3 And a2==b4};

These equations can be seen as simple simplification rules, the
left hand side showing the old value, and the right hand side
showing the new value. Interpreted in that way, {Where} 
is a little simplifier for expressions, using values found by Solve.

Assigning values to the variables values globally can be handled with
an expression like

	solns := Solve(equations,{var1,var2});
	{var1,var2} := Transpose({var1,var2} Where solns);

Multiple sets of values can be applied:

	In> x^2+y^2 Where {x==2 And y==2,x==3 And y==3}
	Out> {8,18};

This assigns the the variables lists of values. These variables
can then be inserted into other expressions, where threading will
fill in all the solutions, and return all possible answers.
	
Groups of equations can be combined, with

	Equations := EquationSet1 AddTo EquationSet2

or, 

	Equations := Equations AddTo Solutions;

Where {Solutions} could have been returned by {Solve}. This last
step makes explicit the fact that equations are on a same footing,
mathematically, as solutions to equations, and are just another
way of looking at a problem.

The equations returned can go farther in that multiple solutions
can be returned: if the value of $ x $ is needed and the equation
determining the value of $ x $ is $ x := Abs(a) $, then a set
of returned solutions could look like:

	Solutions := { a>=0 And x==a, a<0 And x== -a }

The semantics of this list is:

	either a >= 0 And x equals a, or
	       a < 0 And x equals -a


When more information is published, for instance the value
of $ a $ has been determined, the sequence for solving this
can look like:

	In> Solve(a==2 AddTo Solutions,{x})
	Out> x==2;

The solution {a<0 And x==-a} can not be satisfied, and thus is removed
from the list of solutions.

Introducing new information can then be done with the AddTo
operator:

	In> Solutions2 := (a==2 AddTo Solutions);
	Out> { a==2 And a>=0 And x==a, a==2
	  And a<0 And x==-a };

In the above case both solutions can not be true any more, and thus
when passing this list to {Solve}:

	In> Solve(Solutions2,{x})
	Out> x==2;

{AddTo} combines multiple equations through a tensor-product like
scheme:

	In> {A==2,c==d} AddTo {b==3 And  d==2}
	Out> {A==2 And b==3 And d==2,c==d
	  And b==3 And d==2};
	In> {A==2,c==d} AddTo {b==3, d==2}
	Out> {A==2 And b==3,A==2 And d==2,c==d
	  And b==3,c==d And d==2};

A list {a,b} means that a is a solution, OR b is a solution.
AddTo then acts as a AND operation:
	(a or b) and (c or d) => 
	(a or b) Addto (c or d) => 
	(a and c) or (a and d) or (b and c) or (b and d)


{Solve} gathers information as a list of identities. The second
argument is a hint as to what it needs to solve for. It can be a list
of variables, but also "Ode" (to solve ordinary differential equations),
"Trig" (to simplify for trigonometric identities), "Exp" to simplify
for expressions of the form $ Exp(x) $, or "Logic" to simplify
expressions containing logic. The "Logic" simplifier also should
deal with $ a > 2 And a < 0 $ which it should be able to reduce 
to {False}.

{Solve} also leaves room for an 'assume' type mechanism, where the
equations evolve to keep track of constraints. When for instance
the equation $ x == Sin(y) $ is encountered, this might result
in a solution set 

	y == ArcSin(x) And x>=-1 And x <= 1


	    Use Case Scenarios

*REM TODO@@@@ THIS SECTION IS GOING TO CONTAIN EXPLICIT EXAMPLES OF
SOLVING INTERESTING SETS OF EQUATIONS, AND SHOWING THAT THE SOLVE
SCHEME IS ACTUALLY EASY TO USE.

*HEAD To be filled in


			The Yacas internal numeric library

		Introduction

Although there are quite good free arbitrary precision arithmetic
libraries out there, {Yacas} comes with its own (default) implementation.
Other libraries can be used too, but the addition of a native 
small arithmetic library reduces dependencies on other packages.

This part describes the way the arithmetic library is embedded into
{Yacas}, and how it can be used to embed other arithmetic libraries.

		The link between the interpreter and the arithmetic library

The interpreter (as of version 1.0.54) has the concept of an atom,
which has a string representation. As soon as a calculation needs
to be performed, the string representation is used to construct
an object representing the number, in an internal representation that
the arithmetic library can work with.

The basic layout is as follows: there is a base class YacasBigNumber,
which is a pure abstract class, and thus needs to be sub-classed.
This class contains the internal representation, and offers simple
arithmetic operations like addition and multiplication through an interface.

With this class there should be a {c} function which can create such
a class given a string representation (and in addition a precision
and number base).

This set up allows for different arithmetic libraries to be used;
each library has to be hidden behind a sub-class of {YacasBigNumber},
and have a function to create an object representing the number.

The number object should be able to render itself back to a string,
which can then be passed back to the interpreter as a result.

		Interface to the objects

The following code demonstrates how to use arithmetic support.

	// Calculate z=x+y where x=10 and y=15
	YacasBigNumberPtr x(MakeNativeNumber("10",100,10));
	YacasBigNumberPtr y(MakeNativeNumber("15",100,10));
	YacasBigNumberPtr z(x->Add(x,y,10));    
	// cast the result to a string
	LispString  str;
	z->ToString(str,10);

This example uses {MakeNativeNumber} to create an object
that uses the native {double} and {long} as representations
for real-valued and integer-valued numbers respectively.

Here the {YacasBigNumberPtr} is a smart pointer which 
automatically cleans up the number after the calculation is
finished. The functions in the class accept arguments of type
{YacasBigNumberPtr}, but return a value of the type
{YacasBigNumber*}. This is done so that function calls can
be nested; the return values are of type {YacasBigNumber*},
but because the parameter type is {YacasBigNumberPtr}
the compiler creates a temporary {YacasBigNumberPtr} with
the {YacasBigNumber*}, passes it in, and when finished destroys
the {YacasBigNumberPtr}. This in effect also cleans up the
original {YacasBigNumber*} if it isn't used any more. 

Call-by-value could have been used, but that is a little bit more
expensive as copy-constructors then need to be called.
The smart pointer is small (4 bytes) and all it does is
increment and decrement a reference count, as opposed to having
to allocate new memory for the number.

		Interface definition

The basic arithmetic class defines some simple arithmetic functions,
from which other more elaborate functions can be built. They
could even be implemented in {Yacas} script, as most time is
spent doing the arithmetic (and the scripts could still be converted
to c++ code). 


<commit_msg>added more detail about the new numerics interface proposal<commit_after>		Designing modules in the Yacas scripting language

	    Introduction

For any software project where the source code grows to
a substantial amount of different modules, there needs to be
a way to define interfaces between the modules, and a way
to make sure the modules don't interact with the environment
in an unintended way. 

One hallmark of a mature programming language is that it 
supports modules, and a way to define its interface while
hiding the internals of the module. This section describes
the mechanisms for doing so in the Yacas scripting language.

	    Demonstration of the problem
 
Unintentional interactions between two modules typically happen 
when the two modules accidentally share a common "global"
resource, and there should be a mechanism to guarantee that this
will not happen.

The following piece of code is a little example that demonstrates
the problem:

	SetExpand(fn_IsString) <-- [expand:=fn;];
	ram(x_IsList)_(expand != "") <-- ramlocal(x);
	expand:="";
	ramlocal(x) := Map(expand,{x});

This little bit of code defines a function {ram} that calls the
function {Map}, passing the argument passed if it is a string, and
if the function to be mapped was set with the {SetExpand} function.
It contains the following flaws:

*	0. {expand} is a global variable with a rather generic name, one
that another module might decide to use.
*	0. {ramlocal} was intended to be used from within this module only, and
doesn't check for correctness of arguments (a small speed up optimization
that can be used for routines that get called often). As it is, it can be
called from other parts, or even the command line.
*	0. the function {ramlocal} has one parameter, named {x}, which is also
generic (and might be used in the expression passed in to the function),
and {ramlocal} calls {Map}, which calls {Eval} on the arguments. 

The above code can be entered into a file and loaded from the command
line at leisure. Now, consider the following command line interaction
after loading the file with the above code in it:

	In> ramlocal(a)         
	In function "Length" : 
	bad argument number 1 (counting from 1)
	Argument matrix[1] evaluated to a
	In function call  Length(a)
	CommandLine(1) : Argument is not a list

We called {ramlocal} here, which should not have been allowed.

	In> ram(a)
	Out> ram(a);

The function {ram} checks that the correct arguments are passed in
and that {SetExpand} was called, so it will not evaluate if these
requirements are not met. 

Here are some lines showing the functionality of this code as
it was intended to be used:

	In> SetExpand("Sin")
	Out> "Sin";
	In> ram({1,2,3})
	Out> {Sin(1),Sin(2),Sin(3)};

The following piece of code forces the functionality to break
by passing in an expression containing the variable {x}, which
is also used as a parameter name to {ramlocal}.

	In> ram({a,b,c})
	Out> {Sin(a),Sin(b),Sin(c)};
	In> ram({x,y,z})
	Out> {{Sin(x),Sin(y),Sin(z)},Sin(y),Sin(z)};

This result is obviously wrong, comparing it to the call above.
The following shows that the global variable {expand} is exposed
to its environment:

	In> expand
	Out> "Sin";


	    Declaring resources to be local to the module

The solution to the problem is {LocalSymbols}, which changes every
symbol with a specified name to a unique name that could never
be entered by the user on the command line and guarantees that it
can never interact with the rest of the system. The following code
snippet is the same as the above, with the correct use of {LocalSymbols}:


	LocalSymbols(x,expand,ramlocal) [
	  SetExpand(fn_IsString) <-- [expand:=fn;];
	  ram(x_IsList)_(expand != "") <-- ramlocal(x);
	  expand:="";
	  ramlocal(x) := Map(expand,{x});
	];


This version of the same code declares the symbols {x}, {expand}
and {ramlocal} to be local to this module.

With this the interaction becomes a little bit more predictable:

	In> ramlocal(a)
	Out> ramlocal(a);
	In> ram(a)
	Out> ram(a);
	In> SetExpand("Sin")
	Out> "Sin";
	In> ram({1,2,3})
	Out> {Sin(1),Sin(2),Sin(3)};
	In> ram({a,b,c})
	Out> {Sin(a),Sin(b),Sin(c)};
	In> ram({x,y,z})
	Out> {Sin(x),Sin(y),Sin(z)};
	In> expand
	Out> expand;


	    When to use and when not to use {LocalSymbols}

The {LocalSymbols} should ideally be used for every global variable,
for functions that can only be useful within the module and thus
should not be used by other parts of the system, 
and for local variables that run the risk of being passed into 
functions like {Eval}, {Apply}, {Map}, etcetera (functions that
re-evaluate expressions).

A rigorous solution to this is to make all parameters to functions
and global variables local symbols by default, but this might cause
problems when this is not required, or even wanted, behaviour.

The system will never be able to second-guess which function
calls can be exposed to the outside world, and which ones should
stay local to the system. It also goes against a design rule of Yacas:
everything is possible, but not obligatory. This is important 
at moments when functionality is not wanted, as it can be hard 
to disable functionality when the system does it automatically.

There are more caveats: if a local variable is made unique with 
{LocalSymbols}, other routines can not reach it by using the 
{UnFence} construct. This means that {LocalSymbols} is not always 
wanted. 

Also, the entire expression on which the {LocalSymbols} command works
is copied and modified before being evaluated, making loading
time a little slower. This is not a big problem, because the 
speed hit is usually during calculations, not during loading, but
it is best to keep this in mind and keep the code passed to 
{LocalSymbols} concise.








		How Yacas Deals With Sets of Solutions

	    Introduction

*REM @@@@@@@@@@@@@@@@@@@@@@@@
THIS IS A DRAFT, THE FUNCTIONALITY DESCRIBED BELOW IS NOT FULLY
IMPLEMENTED YET!!! THIS IS JUST A PROPOSAL FOR HOW SOLVE MIGHT
WORK.

*HEAD (This is a draft)

Worries: 

*	need to change all code that uses Solve
*	need a lot of changes in documentation
*	arguments to solve are a bit more verbose than the previous
version: Solve({eq1,eq2},vars) versus Solve(eq1 And eq2,vars). Suddenly
things like Solve(leftlist==rightlist,vars) is not possible any more.
This has to be done with extra commands (which is ok?). It can not
stay the way it was, because lists now mean something else, a collection
of disjunct solutions.

*REM @@@@@@@@@@@@@@@@@@@@@@@@

The difference between a problem stated and a solution given is
a subtle one. From a mathematical standpoint, 

	In> Integrate(x,0,B)Cos(x)
	Out> Sin(B);

And thus

	Integrate(x,0,B)Cos(x) == Sin(B)

is a true statement. Furthermore, the left hand side is mathematically
equivalent to the right hand side. Working out the integration, to
arrive at an expression that doesn't imply integration any more is
generally perceived to be a more desirable result, even though
the two sides are equivalent mathematically.

This implies that the statement of a set of equations declaring
equalities is on a same footing as the resulting equations stating
a solution: 

$$ a*x+b==c => x==(c-b)/a $$. 

If the value of x is needed, the right hand side is more desirable.

Viewed in this way, the responsibility of a {Solve} function could
be to manipulate a set of equations in such a way that a certain
piece of information can be pried from it (in this case the value
of $ x == x(a,b,c) $.

A next step is to be able to use the result returned by a {Solve}
operation. 

	    Implementation Semantics of Solve in Yacas

Suppose there is a set of variables that has a specific combination
of solutions and these solutions need to be filled in in an expression:
the {Where} operator can be used for this:

	In> x^2+y^2 Where x==2 And y==3
	Out> 13;

Solve can return one such solution tuple, or a list of tuples.
The list of equations can be passed in to Solve in exactly the same
way. Thus:

	In> Solve(eq1,var)
	Out> a1==b1;
	In> Solve(eq1 And eq2 And eq3,varlist)
	Out> {a1==b1 And a2==b2,a1==b3 And a2==b4};

These equations can be seen as simple simplification rules, the
left hand side showing the old value, and the right hand side
showing the new value. Interpreted in that way, {Where} 
is a little simplifier for expressions, using values found by Solve.

Assigning values to the variables values globally can be handled with
an expression like

	solns := Solve(equations,{var1,var2});
	{var1,var2} := Transpose({var1,var2} Where solns);

Multiple sets of values can be applied:

	In> x^2+y^2 Where {x==2 And y==2,x==3 And y==3}
	Out> {8,18};

This assigns the the variables lists of values. These variables
can then be inserted into other expressions, where threading will
fill in all the solutions, and return all possible answers.
	
Groups of equations can be combined, with

	Equations := EquationSet1 AddTo EquationSet2

or, 

	Equations := Equations AddTo Solutions;

Where {Solutions} could have been returned by {Solve}. This last
step makes explicit the fact that equations are on a same footing,
mathematically, as solutions to equations, and are just another
way of looking at a problem.

The equations returned can go farther in that multiple solutions
can be returned: if the value of $ x $ is needed and the equation
determining the value of $ x $ is $ x := Abs(a) $, then a set
of returned solutions could look like:

	Solutions := { a>=0 And x==a, a<0 And x== -a }

The semantics of this list is:

	either a >= 0 And x equals a, or
	       a < 0 And x equals -a


When more information is published, for instance the value
of $ a $ has been determined, the sequence for solving this
can look like:

	In> Solve(a==2 AddTo Solutions,{x})
	Out> x==2;

The solution {a<0 And x==-a} can not be satisfied, and thus is removed
from the list of solutions.

Introducing new information can then be done with the AddTo
operator:

	In> Solutions2 := (a==2 AddTo Solutions);
	Out> { a==2 And a>=0 And x==a, a==2
	  And a<0 And x==-a };

In the above case both solutions can not be true any more, and thus
when passing this list to {Solve}:

	In> Solve(Solutions2,{x})
	Out> x==2;

{AddTo} combines multiple equations through a tensor-product like
scheme:

	In> {A==2,c==d} AddTo {b==3 And  d==2}
	Out> {A==2 And b==3 And d==2,c==d
	  And b==3 And d==2};
	In> {A==2,c==d} AddTo {b==3, d==2}
	Out> {A==2 And b==3,A==2 And d==2,c==d
	  And b==3,c==d And d==2};

A list {a,b} means that a is a solution, OR b is a solution.
AddTo then acts as a AND operation:
	(a or b) and (c or d) => 
	(a or b) Addto (c or d) => 
	(a and c) or (a and d) or (b and c) or (b and d)


{Solve} gathers information as a list of identities. The second
argument is a hint as to what it needs to solve for. It can be a list
of variables, but also "Ode" (to solve ordinary differential equations),
"Trig" (to simplify for trigonometric identities), "Exp" to simplify
for expressions of the form $ Exp(x) $, or "Logic" to simplify
expressions containing logic. The "Logic" simplifier also should
deal with $ a > 2 And a < 0 $ which it should be able to reduce 
to {False}.

{Solve} also leaves room for an 'assume' type mechanism, where the
equations evolve to keep track of constraints. When for instance
the equation $ x == Sin(y) $ is encountered, this might result
in a solution set 

	y == ArcSin(x) And x>=-1 And x <= 1


	    Use Case Scenarios

*REM TODO@@@@ THIS SECTION IS GOING TO CONTAIN EXPLICIT EXAMPLES OF
SOLVING INTERESTING SETS OF EQUATIONS, AND SHOWING THAT THE SOLVE
SCHEME IS ACTUALLY EASY TO USE.

*HEAD To be filled in


			The Yacas internal numeric library

		Introduction

Although there are quite good free arbitrary precision arithmetic
libraries out there, {Yacas} comes with its own (default) implementation.
Other libraries can be used too, but the addition of a native 
small arithmetic library reduces dependencies on other packages.

This part describes how the arithmetic library is embedded into
{Yacas}, and how to embed other arithmetic libraries.

		The link between the interpreter and the arithmetic library

The interpreter (as of version 1.0.54) has the concept of an <i>atom</i>, an object
which has a string representation. As soon as a calculation needs
to be performed, the string representation is used to construct
an object representing the number, in an internal representation that
the arithmetic library can work with.

The basic layout is as follows: there is a base class {NumberBase},
which is a pure abstract class.
This class offers basic numerical functions,
arithmetic operations such as addition and multiplication, through a set of pure virtual methods.

Integers and floating-point numbers are to be handled by the same class.

An actual number class will be a subclass of {NumberBase} that implements a particular internal representation of numbers and 
With a subclass of {NumberBase} one should supply a {C++} function which can create such
a class given a string representation (and in addition a precision
and number base).

This setup allows different arithmetic libraries to be used;
each library has to be hidden behind a sub-class of {NumberBase}
and have a function to create an object representing the number.

The number object should be able to render itself back to a string,
which can then be passed back to the interpreter as a result.

		Interface to the objects

The following code demonstrates how to use the number objects.

	// Calculate z=x+y where x=10 and y=15
	YacasBigNumberPtr x(MakeNativeNumber("10",100,10));
	YacasBigNumberPtr y(MakeNativeNumber("15",100,10));
	YacasBigNumberPtr z(x->Add(x,y,10));    
	// cast the result to a string
	LispString  str;
	z->ToString(str,10);

This example uses {MakeNativeNumber} to create an object
that uses the native {double} and {long} as representations
for real-valued and integer-valued numbers respectively.

Here the {YacasBigNumberPtr} is a smart pointer which 
automatically cleans up the number after the calculation is
finished. The functions in the class accept arguments of type
{YacasBigNumberPtr}, but return a value of the type
{YacasBigNumber*}. This is done so that function calls can
be nested; the return values are of type {YacasBigNumber*},
but because the parameter type is {YacasBigNumberPtr}
the compiler creates a temporary {YacasBigNumberPtr} with
the {YacasBigNumber*}, passes it in, and when finished destroys
the {YacasBigNumberPtr}. This in effect also cleans up the
original {YacasBigNumber*} if it isn't used any more. 

Call-by-value could have been used instead of pointers, but that is a little bit more
expensive as copy-constructors then need to be called.
The smart pointer is small (4 bytes on 32-bit platforms) and all it does is
increment and decrement a reference count, as opposed to having
to allocate new memory for the number.

		Interface definition

The basic arithmetic class defines some simple arithmetic operations,
from which other more elaborate functions can be built.
*REM They
could even be implemented in {Yacas} script, as most time is
spent doing the arithmetic (and the scripts could still be converted
to C++ code). 

The basic arithmetic operations that need to be implemented by the arithmetic library are:
*	Copy a number, {x := y}
*	Compare two numbers for equality, {x = y}
*	Compute the integer part of the number {x}
*	Check that the number {x} is an integer
*	Add two numbers, {x := y+z}
*	Negate a number, {x := -y}
*	Multiply two numbers, {x := y*z}
*	Multiply two numbers and add to the third, {x := x+y*z}
*	Divide two numbers, {x := y/z}
*	Obtain the sign of the number {x}
*	Obtain an approximate representation of {x} as double-precision number
*	Obtain the "bit count" of the number {x} (more precisely, the integer part of the binary logarithm of the absolute value of {x}).
For integers, this function counts the significant bits.
For floating-point numbers, this function should return the binary exponent of the number (with sign).
*	Bit-shift the number (multiply or divide by 2), {x := x >> n}, {x := x << n} (for integers as well as for non-integers)
*	Perform bitwise arithmetic {x := y&z}, {x := y|z}, {x := y^z} (for integers only)

All other functions such as power, logarithm, cosine and so on, can be efficiently implemented using this basic interface.
{'fraction_non_alphanumeric': 0.07445176139633015, 'fraction_numerical': 0.008087718176587174, 'mean_word_length': 4.313912009512485, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 121, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6484225', 'n_tokens_mistral': 9423, 'n_tokens_neox': 8840, 'n_words': 4848}