package org.geogebra.web.cas.giac;
import java.util.ArrayList;
import org.geogebra.common.cas.CASparser;
import org.geogebra.common.cas.Evaluate;
import org.geogebra.common.cas.giac.CASgiac;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.main.App;
import org.geogebra.common.main.Feature;
import org.geogebra.common.util.debug.Log;
import org.geogebra.web.html5.Browser;
import org.geogebra.web.resources.JavaScriptInjector;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback;
/**
* Web implementation of Giac CAS
*
* @author Michael Borcherds, based on Reduce version
*
*/
public class CASgiacW extends CASgiac {
/** kernel */
Kernel kernel;
/** flag indicating that JS file was loaded */
boolean casLoaded = false;
private Evaluate giac;
/**
* Creates new CAS
*
* @param casParser
* parser
* @param parserTools
* scientific notation convertor
* @param kernel
* kernel
*/
public CASgiacW(CASparser casParser,
Kernel kernel) {
super(casParser);
this.kernel = kernel;
App.setCASVersionString("Giac/JS");
Log.debug("starting CAS");
if (Browser.externalCAS()) {
Log.debug("switching to external");
// CASgiacW.this.kernel.getApplication().getGgbApi().initCAS();
this.casLoaded = true;
} else if (Browser.supportsJsCas()) {
initialize();
}
}
@Override
public String evaluateCAS(String exp) {
if (!casLoaded) {
return "?";
}
try {
// replace Unicode when sending to JavaScript
// (encoding problem)
String processedExp = casParser.replaceIndices(exp, true);
String ret = evaluateRaw(processedExp);
return postProcess(ret);
// } catch (TimeoutException toe) {
// throw new Error(toe.getMessage());
} catch (Throwable e) {
Log.debug("evaluateGiac: " + e.getMessage());
return "?";
}
}
@Override
protected synchronized String evaluate(String exp, long timeoutMilliseconds) {
if (!casLoaded) {
return "?";
}
if (Browser.externalCAS()) {
// native Giac so need same initString as desktop
nativeEvaluateRaw(initString, false);
} else {
// #5439
// restart Giac before each call
nativeEvaluateRaw(initStringWeb, false);
}
// GGB-850
CustomFunctions[] init = CustomFunctions.values();
CustomFunctions.setDependencies();
// Log.debug("exp = " + exp);
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))) {
nativeEvaluateRaw(function.definitionString, false);
/* 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);
nativeEvaluateRaw(dep.definitionString, false);
}
}
// Log.debug("sending " + function);
} else {
// Log.error("not sending " + function + " "
// + function.functionName);
}
// Log.error(function.functionName + " " +
// function.definitionString);
}
nativeEvaluateRaw("timeout " + (timeoutMilliseconds / 1000), false);
// make sure we don't always get the same value!
int seed = rand.nextInt(Integer.MAX_VALUE);
nativeEvaluateRaw("srand(" + seed + ")", false);
// show logging in tube-beta only
String ret = nativeEvaluateRaw(wrapInevalfa(exp), kernel
.getApplication().has(Feature.TUBE_BETA));
return ret;
}
private native String nativeEvaluateRaw(String s, boolean showOutput) /*-{
if (typeof $wnd.evalGeoGebraCASExternal === 'function') {
return $wnd.evalGeoGebraCASExternal(s);
}
if (typeof Float64Array === 'undefined') {
$wnd.console.log("Typed arrays not supported, Giac won't work");
return "?";
}
if (showOutput) {
$wnd.console.log("js giac input:" + s);
}
caseval = $wnd.__ggb__giac.cwrap('caseval', 'string', [ 'string' ]);
var ret = caseval(s);
if (showOutput) {
$wnd.console.log("js giac output:" + ret);
}
return ret
}-*/;
public void initialize() {
GWT.runAsync(new RunAsyncCallback() {
@Override
public void onSuccess() {
Log.debug("giac.js loading success");
JavaScriptInjector.inject(CASResources.INSTANCE.giacJs());
CASgiacW.this.casLoaded = true;
CASgiacW.this.kernel.getApplication().getGgbApi().initCAS();
}
@Override
public void onFailure(Throwable reason) {
Log.debug("giac.js loading failure");
}
});
}
@Override
public void clearResult() {
// not needed
}
@Override
public boolean isLoaded() {
return casLoaded;
}
public boolean externalCAS() {
return Browser.externalCAS();
}
}