The method Scanner.start() parses the input string and returns an object:
Scanner scan = new Scanner("100");
HObject eval_integer = scan.start();
In the above case eval_integer is an instance of HInteger.
Scanner scan = new Scanner("1+a+b+f(x)+Sin(0)");
HObject eval_function = scan.start();
In this case eval_function is an instance of HFunction.
Now the system must evaluate the given object. (in the following we will take eval_function).
HObject result = C.EV(eval_function);
With the toString() method we can turn back the result in a user readable String:
result.toString();
In the method com.hartmath.lib.C.EV() the method com.hartmath.lib.C.ELoop() will be called. In general all following (recursive) evaluation steps can return the special value null, to indicate that the input-expression is the same as the output-expression. So you always have to test if an evaluation function returns null. null means "no result" e.g. if you evaluate Sin(x) you get the result null
return ((result = ELoop(eval_function)) == null) ? eval_function : result;
In the evaluation loop the object will evaluate through the method HObject.evaluate() until HObject.evaluate() returns null.
static public HObject ELoop(HObject obj) { ... HObject result = obj.evaluate(); if (result == null) { ..... return null; } ... HObject temp = result.evaluate(); iterationCounter = 0; while (temp != null) { ... result = temp; temp = result.evaluate(); } ... return result; }
As an example for an atomic object (e.g. HDouble, HDoubleComplex, HInteger, HFraction, HComplex, HString, HSymbol or HPattern object) take a look at the com.hartmath.expression.HInteger.evaluate() method:
public HObject evaluate() { if (C.numericFlag) { return new HDouble(doubleValue()); } return null; }
Only in numeric mode (C.numericFlag==true) the method returns a result (e.g. converts the integer to a double value). (You can start a numeric evaluation with the method com.hartmath.lib.C.NEV() )
Now we take a look at the com.hartmath.expression.HFunction.evaluate() method:
public HObject evaluate() { return symbol.evaluateFunction(this); }
Every HFunction has a variable which is called the "header symbol".
Example: if you have a function Sin(x) the "head" of the function is the HSymbol com.hartmath.lib.C.Sin.
The above call jumps into the HSymbol.evaluateFunction() method:
public HObject evaluateFunction(HFunction f) { HObject result = null; if ((attr & HSymbol.FASTCALL) == HSymbol.FASTCALL) { return functionEval.evaluate(f); } ...
In the int variable attr all attributes (e.g. FASTCALL, ONEIDENTITY, FLAT, ORDERLESS, LISTABLE,...) of this "header symbol" are stored as single bitflags.
A lot of system functions, like Do, While, For ... set the flag "FASTCALL", to shorten the evaluation procedure.
... if ((result = C.ELoop(this)) != null) { return f.apply(result); } ...
If the header can be evaluated it will be "applied" on the given funtion.
Example: suppose you have defined f as a pure function
f=(#+1)&
and now you evaluate the following input:
f(x)
you get the result
x+1
So the call to the HFunction.apply() method will look like this in pseudo code:
"f(x)".apply("(#+1)&");
In the following source code of the HSymbol.evaluateFunction() method, the program gets the number of arguments of the function (int variable sz). For functions, which have only 1 or 2 arguments special evaluation functions are called for a better performance of the system. The HFunction.evaluateAttributtes() method evaluates all arguments of the function recursively (with method com.hartmath.lib.C.EV()) and takes into account the attributes: ONEIDENTITY, FLAT, LISTABLE, ORDERLESS, HOLDFIRST, HOLDREST, HOLDALL.
... int sz = f.size(); if (sz >= 1) { if (sz == 1) { result = f.evaluateAttributes1(); } else { if (sz == 2) { result = f.evaluateAttributes2(); } else { result = f.evaluateAttributes(); } } } if (result != null) { return result; } ...
Some "header symbols" have the attribute BUITLINFIRST so their built in functions will be called before the evaluation of the user defined rules:
... if ((attr & HSymbol.BUILTINFIRST) == HSymbol.BUILTINFIRST) { if ((result = functionEval.evaluate(f)) != null) { return result; } } if (sz > 0) { HObject elem; ...
Now all user defined "UpRules" will be evaluated.
These rules are all defined with the operators ^= or ^:= which assiciate a rule with the headers of the arguments
Example:
Infinity*Infinity^(-1)^:=Throw(InfinityError,Multiply);
... if ((HSymbol.FLAT & attr) != HSymbol.FLAT) { elem = f.get(0); if (elem instanceof HSymbol) { result = ((HSymbol) elem).getUpRule(f); } else { result = elem.head().getUpRule(f); } if (result != null) { return result; } } else { // associative HArrayList arr = new HArrayList(); elem = f.get(0); HObject last; if (elem instanceof HSymbol) { last = elem; if (!((HSymbol) last).isEmptyUpRule()) { arr.add(last); } } else { last = elem.head(); if (!((HSymbol) last).isEmptyUpRule()) { arr.add(last); } } for (int i = 1; i < sz; i++) { elem = f.get(i); if (elem instanceof HSymbol) { if (elem == last) { continue; } last = elem; if (((HSymbol) last).isEmptyUpRule()) { continue; } arr.add(last); } else { if (elem.head() == last) { continue; } last = elem.head(); if (((HSymbol) last).isEmptyUpRule()) { continue; } arr.add(last); } } arr.sort(); if (arr.size() > 0) { last = arr.get(0); if ((result = ((HSymbol) last).getUpRule(f)) != null) { return result; } for (int i = 1; i < arr.size(); i++) { if (!arr.get(i).equals(last)) { last = arr.get(i); if ((result = ((HSymbol) last).getUpRule(f)) != null) { return result; } } } } } } ...
Now all user defined "DownRules" will be evaluated.
These Rules are all defined with the operators = or :=
Examples:
f(x_+y_):=(x*y)/(x+y) or
g(2,3)=100
... if ((result = getDownRule(f)) != null) { return result; } ...
Now all "hard coded java functions" will be evaluated:
Normally these functiions are evaluated in the packages:
com.hartmath.initial or
com.hartmath.loadable
A simple "builtin function" is defined for example in the class:
com.hartmath.loadable.EExec
for the "function symbol"
com.hartmath.lib.C.Exec
... if ((attr & HSymbol.BUILTINFIRST) != HSymbol.BUILTINFIRST) { return functionEval.evaluate(f); } return null; }