Jump to content

User:Mafs/Computer algebra

From Meta, a Wikimedia project coordination wiki
 
A proposal to move this page to MediaWiki.org was rejected.

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

File:Maxima question.png

<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

File:Computer algebra.png


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>&lt;Algebra&gt;</h2><h3>Maximas error message or question:</h3><pre>$maxima_response</"."pre><h2>&lt;/Algebra&gt;</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;
}
?>