package org.geogebra.common.cas.giac;
import java.util.ArrayList;
import org.geogebra.common.cas.CASparser;
import org.geogebra.common.cas.error.TimeoutException;
import org.geogebra.common.cas.giac.binding.CASGiacBinding;
import org.geogebra.common.cas.giac.binding.Context;
import org.geogebra.common.cas.giac.binding.Gen;
import org.geogebra.common.util.StringUtil;
import org.geogebra.common.util.debug.Log;
public abstract class CASgiacB extends CASgiac {
/**
* Giac's context.
*/
private Context context;
protected String threadResult;
public CASgiacB(CASparser casParser) {
super(casParser);
createContext();
}
protected abstract CASGiacBinding createBinding();
protected void createContext() {
try {
CASGiacBinding binding = createBinding();
context = binding.createContext();
} catch (Throwable e) {
Log.error("CAS not available: " + e.getMessage());
}
}
@Override
final public void clearResult() {
threadResult = null;
}
/**
* @param exp0 String to send to Giac
* @param timeoutMilliseconds timeout in milliseconds
* @return String from Giac
*/
final String evalRaw(String exp0, long timeoutMilliseconds) {
CASGiacBinding binding = createBinding();
// #5439
// reset Giac before each call
init(exp0, timeoutMilliseconds);
String exp = wrapInevalfa(exp0);
Log.debug("giac evalRaw input: " + StringUtil.toJavaString(exp));
Gen g = binding.createGen("caseval(" + exp + ")", context);
g = g.eval(1, context);
String ret = g.print(context);
Log.debug("giac evalRaw output: " + ret);
if (ret != null && ret.startsWith("\"") && ret.endsWith("\"")) {
ret = ret.substring(1, ret.length() - 1);
}
return ret;
}
private void init(String exp, long timeoutMilliseconds) {
CASGiacBinding binding = createBinding();
Gen g = binding.createGen(initString, context);
g.eval(1, context);
CustomFunctions[] init = CustomFunctions.values();
CustomFunctions.setDependencies();
for (int i = 0; i < init.length; i++) {
CustomFunctions function = init[i];
// send only necessary init commands
boolean foundInInput = false;
/* This is very hacky here. If the input expression as string
* contains an internal GeoGebra CAS command, then that command will be executed
* in Giac. TODO: find a better a way.
*/
if (function.functionName == null
|| (foundInInput = (exp
.indexOf(function.functionName) > -1))) {
g = binding.createGen(function.definitionString, context);
g.eval(1, context);
/* Some commands may require additional commands to load. */
if (foundInInput) {
ArrayList<CustomFunctions> dependencies = CustomFunctions
.prereqs(function);
for (CustomFunctions dep : dependencies) {
Log.debug(function + " implicitly loads " + dep);
g = binding.createGen(dep.definitionString, context);
g.eval(1, context);
}
}
}
}
g = binding.createGen("\"timeout " + (timeoutMilliseconds / 1000) + "\"", context);
g.eval(1, context);
// make sure we don't always get the same value!
int seed = rand.nextInt(Integer.MAX_VALUE);
g = binding.createGen("srand(" + seed + ")", context);
g.eval(1, context);
}
@Override
public String evaluateCAS(String input) {
// don't need to replace Unicode when sending to JNI
String exp = casParser.replaceIndices(input, false);
try {
return evaluate(exp, timeoutMillis);
} catch (TimeoutException te) {
throw te;
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
@Override
protected String evaluate(final String exp, final long timeoutMillis0)
throws Throwable {
Runnable evalFunction = new Runnable() {
@Override
public void run() {
threadResult = evalRaw(exp, timeoutMillis0);
}
};
threadResult = null;
callEvaluateFunction(evalFunction);
String ret = postProcess(threadResult);
// Log.debug("giac output: " + ret);
if (ret.contains("user interruption")) {
Log.debug("Standard timeout from Giac");
throw new TimeoutException("Standard timeout from Giac");
}
return ret;
}
protected abstract void callEvaluateFunction(Runnable evaluateFunction) throws Throwable;
public boolean externalCAS() {
return true;
}
}