User:Mafs/Computer algebra
Installation
- enable math in mediawiki
- get a copy of the maxima software from http://maxima.sourceforge.net
- put the php script as Maxima.php into the extension directory
- within the scipt adapt the path to the maxima executable
- add the line
require_once("extension/Maxima.php");
- to your LocalSettings.php
- The script is not well tested. It is **not** recommended to use the script on a public site.
- A quote from the maxima mailing list:
I'd be careful about using this script, or scripts like it. In particular the variable $maxima_blacklist does not appear to contain the Maxima "system" command. It appears that an arbitrary user would be able to execute a system command, which (at a very quick first read through) represents a rather large security hole.
- So, although "system" has been added to the list, do only use the script for experiments on localhost! Mafs 20:30, 1 March 2006 (UTC)
Syntax
<Algebra> </Algebra> defines a maxima session. Any line terminating with a semicolon is passed to maxima for evaluation. For all other lines the wiki syntax applies.
The maxima syntax is described on http://maxima.sourceforge.net/docs.shtml.
If the maxima session terminates with an error or a question, the raw output of maxima will be displayed. For details how to avoid such questions see the maxima documentation.
Example
<Algebra> Equation 1 eq1: x^2 + 3*x*y + y^2 = 0; Equation 2 eq2: 3*x + y = 1; Solution solve([eq1, eq2]); ---- Integration f(x) := exp( sin(x)); integrate(f(x), x , 0, %pi); </Algebra>
Screen shoot
PHP Script
- Patch avoiding the use of $wgOut->parse() that spoils other math tags that may exist on the same page.
Since the parse() function can not be invoked twice on the same page, change on the code bellow
$tex_math = "$indent<math>".$m[1]."</math>\n"
by
$tex_math = $indent.MathRenderer::renderMath($m[1])."\n";
and remove
$text = $wgOut->parse($text);
replacing the parse() function by the explicit <math> parsing.
- Patch correcting the error return if multiple Algebra environments exist on the same page.
Since the variable $maxima_session_result is global, its value is kept within different Algebra blocks in the same page. If must be rest to false for each block. Change
$maxima_input = implode(" ", $list);
$maxima_response = maxima_process($maxima_input);
by
$maxima_input = implode(" ", $list);
$maxima_session_result=false;
$maxima_response = maxima_process($maxima_input);
- Patch correcting the bug filters out the whole output of maxima.
Change
$maxima_response = preg_replace("|.*$unique_md5.in\s*\n(.*?)|s", "\\1", $maxima_response);
to
$maxima_response = preg_replace("|^.*?$unique_md5.in\s*\n(.*?)|s", "\\1", $maxima_response);
Attention: For copying click on edit and copy from the source text.
<?php
/*
Computer Algebra - Maxima - Mediawiki
M. Arndt <chmarndt at medemsand.de> (February 2006)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
if( !defined( 'MEDIAWIKI' ) ) {
die();
}
$wgExtensionCredits['other'][] = array(
'name' => 'Algebra extension',
'author' => 'Markus Arndt',
'version' => 'February 2006',
'url' => 'http://meta.wikimedia.org/wiki/User:Mafs/Computer_algebra',
'description' => 'using http://maxima.sourceforge.net algebraic expressions are evaluated and displayed'
);
$wgExtensionFunctions[] = "wfAlgebraExtension";
function wfAlgebraExtension() {
global $wgParser;
$wgParser->setHook( "Algebra", "renderAlgebra" );
}
function renderAlgebra( $text ){
global $wgMathPath, $wgOut, $wgUploadDirectory, $maxima_session_result;
$list = array();
// Extract the maxima expressions and replace them by placehoders.
$i = 0;
while(preg_match("|^(.*?([;$]))\s*$|m", $text, $t)) {
if($t[2] == ';') {
$list[] = $t[1]." tex(%);";
$text = str_replace($t[0], "algebra_".md5($i), $text);
$i++;
} else {
$list[] = $t[1];
$text = str_replace($t[0], "", $text);
}
}
$maxima_input = implode(" ", $list);
$maxima_response = maxima_process($maxima_input);
if ($maxima_session_result) {
$maxima_response = str_replace("$$", "--n9853g204zh--", $maxima_response);
$maxima_response = str_replace("|\n", "--n9853g204zh--", $maxima_response);
// Replace the placeholders by maximas tex expressions, add math-tags
$i = 0;
while(preg_match("/--n9853g204zh--(.*?)--n9853g204zh--/is", $maxima_response, $m)) {
$tex_math = "$indent<math>".$m[1]."</math>\n";
$maxima_response = substr($maxima_response, 0, strpos($maxima_response, $m[0])).substr($maxima_response, strpos($maxima_response, $m[0])+strlen($m[0]));
$text = str_replace("algebra_".md5($i), $tex_math, $text);
$i++;
}
$text = $wgOut->parse($text);
} else {
$text = "<h2><Algebra></h2><h3>Maximas error message or question:</h3><pre>$maxima_response</"."pre><h2></Algebra></h2>";
}
return $text;
}
function maxima_process($text) {
global $maxima_process_message, $maxima_ex_time, $maxima_session_result;
// Some Settings
$maxima_path = "/usr/local/bin/maxima";
$temp_path = "/tmp/maxima/";
$max_execution_time = 5; // in seconds
// As a simplistic security measure, all lines containing one of these expressions
// are not passed to maxima.
$maxima_blacklist = array("save", "load", "plot", "lisp", "includ", "compil", "file", "batch", "stringout", "translat", "stout", "stin", "block", "system");
$maxima_question = false;
$max_execution_time_exceeded = false;
$unique_md5 = md5 (uniqid (rand()));
$maxima_input = $text;
$maxima_process_message = "";
if ( ! is_dir( $temp_path ) ) { mkdir( $temp_path, 0777 ); }
$maxima_input = str_replace("\n", "", $maxima_input);
$lines = explode(";", $maxima_input);
foreach($lines as $line) {
$delete = False;
foreach($maxima_blacklist as $d) {
if (eregi($d, $line, $a)) {
$delete = True;
$maxima_process_message .= "The expression \"".trim($line).";\" has not been passed to maxima.<br>";
break;
}
}
if (!$delete) $newline[] = $line;
}
$maxima_input = implode(";", $newline);
$maxima_input = $maxima_input . " maxima_session_".$unique_md5.";";
$out_file = $temp_path."maxima_$unique_md5.out";
$in_file = $temp_path."maxima_$unique_md5.in";
$pid_file = $temp_path."maxima_$unique_md5.pid";
$fp = fopen($in_file, "w");
fwrite($fp, $maxima_input);
fclose($fp);
$cmd = "env MAXIMA_USERDIR=$temp_path $maxima_path --batch=\"$in_file\" > $out_file &\njobs -l > $pid_file";
// For checking the environment:
// $cmd = "/usr/local/bin/maxima -d";
// return $cmd;
$start_time = time() + microtime();
passthru($cmd, $ret);
// Process Control *****************************************************************
$pf = file_get_contents($pid_file);
preg_match("/^.*? ([0-9]+) .*/", $pf, $p);
$pid = trim($p[1]);
if ($pid > 0 and is_numeric($pid)) {
while( true ) {
$delta_time = time() + microtime() - $start_time;
if ($delta_time > $max_execution_time) $max_execution_time_exceeded = true;
shell_exec("kill -s stop $pid");
$l = shell_exec("wc -l $out_file");
$m = shell_exec("head -n $l $out_file");
shell_exec("kill -s cont $pid");
if (preg_match("/.*\?$/s", trim($m), $a)) $maxima_question = true;
$s = shell_exec("ps -o pid,args -p $pid | grep maxima");
//return $s;
if (!strstr($s, $pid)) break;
if ($max_execution_time_exceeded or $maxima_question) break;
}
$s = shell_exec("ps -o pid,args -p $pid | grep maxima");
if (strstr($s, $pid)) shell_exec("kill $pid");
// Process Control *****************************************************************
$end_time = time() + microtime();
$maxima_ex_time = ($end_time - $start_time);
if (file_exists($out_file) ) $maxima_response = file_get_contents($out_file); else {
$maxima_process_message .= "Maxima did not run properly (1).<br>";
}
// remove repeated questions
if ($maxima_question) {
preg_match("/.*(\n[^\n]*?\?)$/s", trim($m), $a);
$maxima_response = str_replace($a[1], "", $maxima_response);
$maxima_response = trim($maxima_response)."\n\n".trim($a[1]);
}
if (strstr($maxima_response, "maxima_session_".$unique_md5)) $maxima_session_result = true;
// remove headings, tail
if (strstr($maxima_response, $unique_md5)) {
$maxima_response = preg_replace("|.*$unique_md5.in\s*\n(.*?)|s", "\\1", $maxima_response);
$maxima_response = preg_replace("|(.*?)\n[^\n]*?maxima_session_$unique_md5.*|s", "\\1", $maxima_response);
}
if ($max_execution_time_exceeded) $maxima_process_message .= "The maximal allowed execution time has been exceeded.<br>";
} else {
$maxima_process_message .= "Maxima did not run properly (2).<br>";
}
$s = shell_exec("rm ".$temp_path."maxima_".$unique_md5."*");
return $maxima_response;
}
?>