<?php

/****
 * ASCIIMathPHP and associated classes:
 * -- XMLNode
 * -- MathMLNode extends XMLNode
 *
 * These classes are a PHP port of ASCIIMath(c) Peter Jipsen http://www.chapman.edu/~jipsen
 *
 * ASCIIMathPHP Version 1.21.1, 06 Jan 2007, (c) Kee-Lin Steven Chan (kc56@cornell.edu)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program 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
 * General Public License (at http://www.gnu.org/copyleft/gpl.html) 
 * for more details.
 *
 * ChangeLog
 *
 * Ver 1.12.1
 * -- Included the missing setCurrExpr() method
 *
 * Ver 1.12
 * -- Added changes that David Lippman <DLippman@pierce.ctc.edu> made to bring ASCIIMathPHP up to 
 * ASCIIMath 1.4.7 functionality.
 * -- Added parseIntExpr, for intermediate expression parsing rule, allowing x^2/x^3 to render as (x^2)/(x^3)
 * -- Added quotes as another way of designating text; "hello" is equivalent to text(hello)
 * -- Added FUNC designator to allow sin, cos, etc to act as functions, so sin(x)/x renders as {sin(x)}/x
 *
 * Ver 1.11
 * -- Fixed bug that stopped script execution for incomplete expressions
 * -- Changed the algorithm for parsing expressions so that it matches the longest string possible (greedy)
 *
 * Ver 1.10
 * -- Added definition support
 * -- Added stackrel support
 * -- Added a bunch of different symbols etc. >>, << and definitions like dx, dy, dz etc.
 *
 * Ver 1.02
 * -- Fixed bug with mbox and text
 * -- Fixed spacing bug with mbox and text
 *
 * Ver 1.01
 * -- Fixed Bug that did not parse symbols greater than a single character
 * correctly when appearing at end of expression.
 *
 ***/

class XMLNode
{
    
// Private variables
    
var $_id;
    var 
$_name;
    var 
$_content;
    var 
$_mt_elem_flg;
    var 
$_attr_arr;
    var 
$_child_arr;
    var 
$_nmspc;
    var 
$_nmspc_alias;
    var 
$_parent_id;
    var 
$_parent_node;
    
    function & 
XMLNode($id NULL)
    {
        
$this->_id = isset($id) ? $id md5(uniqid(rand(),1));
        
$this->_name '';
        
$this->_content '';
        
$this->_mt_elem_flg FALSE;
        
$this->_attr_arr = array();
        
$this->_child_arr = array();
        
$this->_nmspc '';
        
$this->_nmspc_alias '';
        
$this->_parent_id FALSE;
        
$this->_parent_node NULL;
    }
    
    function 
addChild(&$node)
    {
        
$this->_child_arr[$node->getId()] =& $node;
        
$node->setParentId($this->_id);
        
$node->setParentNode($this);
    }
    
    function 
addChildArr(&$node_arr)
    {
        
$key_arr array_keys($node_arr);
        
$num_key count($key_arr);
        
        for (
$i 0$i $num_key$i++) {
            
$node =& $node_arr[$key_arr[$i]];
            
$this->addChild($node);
        }
    }
    
    function 
insertChildBefore($idx,&$node)
    {
        
$key_arr array_keys($this->_child_arr);
        
$num_key count($key_arr);
        
$tmp_arr arry();
        
        for (
$i 0;$i $num_key;$i++) {
            if (
$i == $idx) {
                
$tmp_arr[$node->getId()] =& $node;
            }
            
$tmp_arr[$key_arr[$i]] =& $this->_child_arr[$key_arr[$i]];
        }
        
$this->_child_arr =& $tmp_arr;
    }
    
    function 
insertChildAfter($idx,&$node)
    {
        
$key_arr array_keys($this->_child_arr);
        
$num_key count($key_arr);
        
$tmp_arr arry();
        
        for (
$i 0;$i $num_key;$i++) {
            
$tmp_arr[$key_arr[$i]] =& $this->_child_arr[$key_arr[$i]];
            if (
$i == $idx) {
                
$tmp_arr[$node->getId()] =& $node;
            }
        }
        
$this->_child_arr =& $tmp_arr;
    }
    
    function 
setId($id)
    {
        
$this->_id $id;
    }
    
    function 
setName($name)
    {
        
$this->_name $name;
    }
    
    function 
setNamepace($nmspc)
    {
        
$this->_nmspc $nmspc;
    }
    
    function 
setNamespaceAlias($nmspc_alias)
    {
        
$this->_nmspc_alias $nmspc_alias;
    }
    
    function 
setContent($content)
    {
        
$this->_content $content;
    }
    
    function 
setEmptyElem($mt_elem_flg)
    {
        
$this->_mt_elem_flg $mt_elem_flg;
    }
    
    function 
setAttr($attr_nm,$attr_val)
    {
        
$this->_attr_arr[$attr_nm] = $attr_val;
    }
    
    function 
setAttrArr($attr_arr)
    {
        
$this->_attr_arr $attr_arr;
    }
    
    function 
setParentId($id)
    {
        
$this->_parent_id $id;
    }
    
    function 
setParentNode(&$node)
    {
        
$this->_parent_node =& $node;
    }
    
    function 
getId()
    {
        return(
$this->_id);
    }
    
    function 
getName()
    {
        return(
$this->_name);
    }
    
    function 
getNamespace()
    {
        return(
$this->_nmspc);
    }
    
    function 
getNamespaceAlias()
    {
        return(
$this->_nmspc_alias);
    }
    
    function 
getContent()
    {
        return(
$this->_content);
    }
    
    function 
getAttr($attr_nm)
    {
        if (isset(
$this->_attr_arr[$attr_nm])) {
            return(
$this->_attr_arr[$attr_nm]);
        } else {
            return(
NULL);
        }
    }
    
    function 
getAttrArr()
    {
        return(
$this->_attr_arr);
    }
    
    function 
getParentId()
    {
        return(
$this->parent_id);
    }
    
    function & 
getParentNode()
    {
        return(
$this->_parent_node);
    }
    
    function & 
getChild($id)
    {
        if (isset(
$this->_child_arr[$id])) {
            return(
$this->_child_arr[$id]);
        } else {
            return(
FALSE);
        }
    }
    
    function & 
getFirstChild()
    {
        
$id_arr array_keys($this->_child_arr);
        
$num_child count($id_arr);
        
        if (
$num_child 0) {
            return(
$this->_child_arr[$id_arr[0]]);
        } else {
            return(
FALSE);
        }
    }
    
    function & 
getLastChild()
    {
        
$id_arr array_keys($this->_child_arr);
        
$num_child count($id_arr);
        
        if (
$num_child 0) {
            return(
$this->_child_arr[$id_arr[$num_child 1]]);
        } else {
            return(
FALSE);
        }
    }
    
    function & 
getChildByIdx($idx)
    {
        
$id_arr array_keys($this->_child_arr);
        
        if (isset(
$this->_child_arr[$id_arr[$idx]])) {
            return(
$this->_child_arr[$id_arr[$idx]]);
        } else {
            return(
FALSE);
        }
    }
    
    function 
getNumChild()
    {
        return(
count($this->_child_arr));
    }
    
    function 
removeChild($id)
    {
        unset(
$this->_child_arr[$id]);
    }
    
    function 
removeChildByIdx($idx)
    {
        
$key_arr array_keys($this->_child_arr);
        unset(
$this->_child_arr[$key_arr[$idx]]);
    }
    
    function 
removeFirstChild()
    {
        
$key_arr array_keys($this->_child_arr);
        unset(
$this->_child_arr[$key_arr[0]]);
    }
    
    function 
removeLastChild()
    {
        
$key_arr array_keys($this->_child_arr);
        unset(
$this->_child_arr[$key_arr[count($key_arr)-1]]);
    }
    
    function 
dumpXML($indent_str "\t")
    {
        
$attr_txt $this->_dumpAttr();
        
$name $this->_dumpName();
        
$xmlns $this->_dumpXmlns();
        
$lvl $this->_getCurrentLevel();
        
$indent str_pad('',$lvl,$indent_str);
        
        if (
$this->_mt_elem_flg) {
            
$tag "$indent<$name$xmlns$attr_txt />";
            return(
$tag);
        } else {
            
$key_arr array_keys($this->_child_arr);
            
$num_child count($key_arr);
            
            
$tag "$indent<$name$xmlns$attr_txt>$this->_content";
            
            for (
$i 0;$i $num_child;$i++) {
                
$node =& $this->_child_arr[$key_arr[$i]];
                
                
$child_txt $node->dumpXML($indent_str);
                
$tag .= "\n$child_txt";
            }
            
            
$tag .= ($num_child "\n$indent</$name>" "</$name>");
            return(
$tag);
        }
    }
    
    function 
_dumpAttr()
    {
        
$id_arr array_keys($this->_attr_arr);
        
$id_arr_cnt count($id_arr);
        
$attr_txt '';
        
        for(
$i 0;$i $id_arr_cnt;$i++) {
            
$key $id_arr[$i];
            
$attr_txt .= " $key=\"{$this->_attr_arr[$key]}\"";
        }
        
        return(
$attr_txt);
    }
    
    function 
_dumpName()
    {
        
$alias $this->getNamespaceAlias();
        if (
$alias == '') {
            return(
$this->getName());
        } else {
            return(
"$alias:" $this->getName());
        }
    }
    
    function 
_dumpXmlns()
    {
        
$nmspc $this->getNamespace();
        
$alias $this->getNamespaceAlias();
        
        if (
$nmspc != '') {
            if (
$alias == '') {
                return(
" xmlns=\"" $nmspc "\"");
            } else {
                return(
" xmlns:$alias=\"" $nmspc "\"");
            }
        } else {
            return(
'');
        }
    }
    
    function 
_getCurrentLevel()
    {
        if (
$this->_parent_id === FALSE) {
            return(
0);
        } else {
            
$node =& $this->getParentNode();
            
$lvl $node->_getCurrentLevel();
            
$lvl++;
            return(
$lvl);
        }
    }
}

class 
MathMLNode extends XMLNode
{
    function & 
MathMLNode($id NULL)
    {
        
parent::XMLNode($id);
    }
    
    function 
removeBrackets()
    {
        if (
$this->_name == 'mrow') {
            if (
$c_node_0 =& $this->getFirstChild()) {
                
$c_node_0->isLeftBracket() ? $this->removeFirstChild() : 0;
            }
            
            if (
$c_node_0 =& $this->getLastChild()) {
                
$c_node_0->isRightBracket() ? $this->removeLastChild() : 0;
            }
        }
    }
    
    function 
isLeftBracket()
    {
        switch (
$this->_content) {
            case 
'{':
            case 
'[':
            case 
'(':
                return(
TRUE);
                break;
        }
        return(
FALSE);
    }
    
    function 
isRightBracket()
    {
        switch (
$this->_content) {
            case 
'}':
            case 
']':
            case 
')':
                return(
TRUE);
                break;
        }
        return(
FALSE);
    }
}

class 
ASCIIMathPHP
{
    var 
$_expr;
    var 
$_curr_expr;
    var 
$_prev_expr;
    var 
$_symbol_arr;
    var 
$_node_arr;
    var 
$_node_cntr;
    
    function 
ASCIIMathPHP($symbol_arr,$expr NULL)
    {
        
$this->_symbol_arr $symbol_arr;
        if (isset(
$expr)) {
            
$this->setExpr($expr);
        }
    }
    
    
/**
     * Returns an empty node (containing a non-breaking space) 26-Apr-2006
     * 
     * Used when an expression is incomplete
     *
     * @return object
     *
     * @access private
     */
    
function & emptyNode()
    {
        
$tmp_node =& $this->createNode();
        
$tmp_node->setName('mn');
        
$tmp_node->setContent('&#' hexdec('200B') . ';');
        return 
$tmp_node;
    }
    
    function 
pushExpr($prefix// 2005-06-11 wes
    
{
        
$this->_curr_expr $prefix $this->_curr_expr;
    }

    function 
setExpr($expr)
    {
        
$this->_expr $expr;
        
$this->_curr_expr $expr;
        
$this->_prev_expr $expr;
        
        
$this->_node_arr = array();
        
$this->_node_cntr 0;
    }
    
    function 
genMathML($attr_arr NULL)
    {
        
// <math> node
        
$node_0 =& $this->createNode();
        
$node_0->setName('math');
        
$node_0->setNamepace('http://www.w3.org/1998/Math/MathML');
        
        
// <mstyle> node
        
if (isset($attr_arr)) {
            
$node_1 =& $this->createNode();
            
$node_1->setName('mstyle');
            
$node_1->setAttrArr($attr_arr);
            
            
$node_arr =& $this->parseExpr();
            
            
$node_1->addChildArr($node_arr);
            
$node_0->addChild($node_1);
        } else {
            
$node_arr =& $this->parseExpr();
            
$node_0->addChildArr($node_arr);
        }
        
        return 
TRUE;
    }
    
    
/*
    function & mergeNodeArr(&$node_arr_0,&$node_arr_1)
    {
        $key_arr_0 = array_keys($node_arr_0);
        $key_arr_1 = array_keys($node_arr_1);
        
        $num_key_0 = count($key_arr_0);
        $num_key_1 = count($key_arr_1);
        
        $merge_arr = array();
        
        for ($i = 0;$i < $num_key_0;$i++) {
            $merge_arr[$key_arr_0[$i]] =& $node_arr_0[$key_arr_0[$i]];
        }
        
        for ($j = 0;$j < $num_key_1;$i++) {
            $merge_arr[$key_arr_1[$i]] =& $node_arr_1[$key_arr_1[$i]];
        }
        
        return($merge_arr);
    }
    */
    
    //Broken out of parseExpr Sept 7, 2006 David Lippman for
    //ASCIIMathML 1.4.7 compatibility
    
function & parseIntExpr()
    {
        
$sym_0 $this->getSymbol();
        
$node_0 =& $this->parseSmplExpr();
        
$sym $this->getSymbol();
        
        if (isset(
$sym['infix']) && $sym['input'] != '/') {
            
$this->chopExpr($sym['symlen']);
            
$node_1 =& $this->parseSmplExpr();
            
            if (
$node_1 === FALSE) { //show box in place of missing argument
                
$node_1 =& $this->emptyNode();//??
            
} else {
                
$node_1->removeBrackets();
            }
            
            
// If 'sub' -- subscript
            
if ($sym['input'] == '_') {
                
                
$sym_1 $this->getSymbol();
                
                
// If 'sup' -- superscript
                
if ($sym_1['input'] == '^') { 
                    
$this->chopExpr($sym_1['symlen']);
                    
$node_2 =& $this->parseSmplExpr();
                    
$node_2->removeBrackets();
                    
                    
$node_3 =& $this->createNode();
                    
$node_3->setName(isset($sym_0['underover']) ? 'munderover' 'msubsup');
                    
$node_3->addChild($node_0);
                    
$node_3->addChild($node_1);
                    
$node_3->addChild($node_2);
                    
                    
$node_4 =& $this->createNode();
                    
$node_4->setName('mrow');
                    
$node_4->addChild($node_3);
                    
                    return 
$node_4;
                } else {
                    
$node_2 =& $this->createNode();
                    
$node_2->setName(isset($sym_0['underover']) ? 'munder' 'msub');
                    
$node_2->addChild($node_0);
                    
$node_2->addChild($node_1);
                    
                    return 
$node_2;
                }
            } else {
                
$node_2 =& $this->createNode();
                
$node_2->setName($sym['tag']);
                
$node_2->addChild($node_0);
                
$node_2->addChild($node_1);
                
                return(
$node_2);
            }
        } elseif (
$node_0 !== FALSE) {
            return(
$node_0);
        } else {
            return 
$this->emptyNode();
        }
        
    }
    
    function & 
parseExpr()
    {
        
// Child/Fragment array
        
$node_arr = array();
        
        
// Deal whole expressions like 'ax + by + c = 0' etc.
        
do {
            
$sym_0 $this->getSymbol();
            
$node_0 =& $this->parseIntExpr();
            
$sym $this->getSymbol();
            
// var_dump($sym);
            
            
if (isset($sym['infix']) && $sym['input'] == '/') {
                
$this->chopExpr($sym['symlen']);
                
$node_1 =& $this->parseIntExpr();
                
                if (
$node_1 === FALSE) { //should show box in place of missing argument
                    
$node_1 =& $this->emptyNode();
                    continue;
                }

                
$node_1->removeBrackets();
                
                
// If 'div' -- divide
                
$node_0->removeBrackets();
                
$node_2 =& $this->createNode();
                
$node_2->setName($sym['tag']);
                
$node_2->addChild($node_0);
                
$node_2->addChild($node_1);
                
$node_arr[$node_2->getId()] =& $node_2;
            
            } elseif (
$node_0 !== FALSE) {
                
$node_arr[$node_0->getId()] =& $node_0;
            }
        } while (!isset(
$sym['right_bracket']) && $sym !== FALSE && $sym['output'] != '');
        
        
//var_dump($sym);
        // Possibly to deal with matrices
        
if (isset($sym['right_bracket'])) {
            
$node_cnt count($node_arr);
            
$key_node_arr array_keys($node_arr);
            
            if (
$node_cnt 1) {
                
$node_5 =& $node_arr[$key_node_arr[$node_cnt-1]];
                
$node_6 =& $node_arr[$key_node_arr[$node_cnt-2]];
            } else {
                
$node_5 FALSE;
                
$node_6 FALSE;
            }
            
            
// Dealing with matrices
            
if ($node_5 !== FALSE && $node_6 !== FALSE &&
                
$node_cnt && 
                
$node_5->getName() == 'mrow' && 
                
$node_6->getName() == 'mo' &&
                
$node_6->getContent() == ',') {
                
                
// Checking if Node 5 has a LastChild
                
if ($node_7 =& $node_5->getLastChild()) {
                    
$node_7_cntnt $node_7->getContent();
                } else {
                    
$node_7_cntnt FALSE;
                }
                
                
// If there is a right bracket
                
if ($node_7 !== FALSE && ($node_7_cntnt == ']' || $node_7_cntnt == ')')) {
                    
                    
// Checking if Node 5 has a firstChild
                    
if ($node_8 =& $node_5->getFirstChild()) {
                        
$node_8_cntnt $node_8->getContent();
                    } else {
                        
$node_8_cntnt FALSE;
                    }
                    
                    
// If there is a matching left bracket
                    
if ($node_8 !== FALSE && 
                        ((
$node_8_cntnt == '(' && $node_7_cntnt == ')' && $sym['output'] != '}') ||
                        (
$node_8_cntnt == '[' && $node_7_cntnt == ']'))) {
                            
                        
$is_mtrx_flg TRUE;
                        
$comma_pos_arr = array();
                        
                        
$i 0;
                        
                        while (
$i $node_cnt && $is_mtrx_flg) {
                            
$tmp_node =& $node_arr[$key_node_arr[$i]];
                            
                            if(
$tmp_node_first =& $tmp_node->getFirstChild()) {
                                
$tnfc $tmp_node_first->getContent();
                            } else {
                                
$tnfc FALSE;
                            }
                            
                            if(
$tmp_node_last =& $tmp_node->getLastChild()) {
                                
$tnlc $tmp_node_last->getContent();
                            } else {
                                
$tnlc FALSE;
                            }
                            
                            if (isset(
$key_node_arr[$i+1])) {
                                
$next_tmp_node =& $node_arr[$key_node_arr[$i+1]];
                                
$ntnn $next_tmp_node->getName();
                                
$ntnc $next_tmp_node->getContent();
                            } else {
                                
$ntnn FALSE;
                                
$ntnc FALSE;
                            }
                            
                            
// Checking each node in node array for matrix criteria
                            
if ($is_mtrx_flg) {
                                
$is_mtrx_flg $tmp_node->getName() == 'mrow' &&
                                    (
$i == $node_cnt-|| $ntnn == 'mo' && $ntnc == ',') &&
                                    
$tnfc == $node_8_cntnt && $tnlc == $node_7_cntnt;
                            }

                            if (
$is_mtrx_flg) {
                                for (
$j 0;$j $tmp_node->getNumChild();$j++) {
                                    
$tmp_c_node =& $tmp_node->getChildByIdx($j);
                                    
                                    if (
$tmp_c_node->getContent() == ',') {
                                        
$comma_pos_arr[$i][] = $j;
                                    }
                                }
                            }    
                            
                            if (
$is_mtrx_flg && $i 1) {
                                
                                
$cnt_cpan = isset($comma_pos_arr[$i]) ? count($comma_pos_arr[$i]) : NULL;
                                
$cnt_cpap = isset($comma_pos_arr[$i-2]) ? count($comma_pos_arr[$i-2]) : NULL;
                                
$is_mtrx_flg $cnt_cpan == $cnt_cpap;
                            }
                            
                            
$i += 2;
                        }
                        
                        
// If the node passes the matrix tests
                        
if ($is_mtrx_flg) {
                            
$tab_node_arr = array();
                            
                            for (
$i 0;$i $node_cnt;$i += 2) {
                                
$tmp_key_node_arr array_keys($node_arr);
                                if (!(
$tmp_node =& $node_arr[$tmp_key_node_arr[0]])) {
                                    break;
                                }
                                
$num_child $tmp_node->getNumChild();
                                
$k 0;
                                
                                
$tmp_node->removeFirstChild();
                                
                                
$row_node_arr = array();
                                
$row_frag_node_arr = array();
                                
                                for (
$j 1;$j < ($num_child-1);$j++) {
                                    if (isset(
$comma_pos_arr[$i][$k]) && 
                                        
$j == $comma_pos_arr[$i][$k]) {
                                        
                                        
$tmp_node->removeFirstChild();
                                        
                                        
$tmp_c_node =& $this->createNode();
                                        
$tmp_c_node->setName('mtd');
                                        
$tmp_c_node->addChildArr($row_frag_node_arr);
                                        
$row_frag_node_arr = array();
                                        
                                        
$row_node_arr[$tmp_c_node->getId()] =& $tmp_c_node;
                                        
                                        
$k++;
                                    } else {
                                        
                                        if (
$tmp_c_node =& $tmp_node->getFirstChild()) {
                                            
$row_frag_node_arr[$tmp_c_node->getId()] =& $tmp_c_node;
                                            
$tmp_node->removeFirstChild();
                                        }
                                    }
                                }
                                
                                
$tmp_c_node =& $this->createNode();
                                
$tmp_c_node->setName('mtd');
                                
$tmp_c_node->addChildArr($row_frag_node_arr);
                                
                                
$row_node_arr[$tmp_c_node->getId()] =& $tmp_c_node;
                                
                                if (
count($node_arr) > 2) {
                                    
$tmp_key_node_arr array_keys($node_arr);
                                    unset(
$node_arr[$tmp_key_node_arr[0]]);
                                    unset(
$node_arr[$tmp_key_node_arr[1]]);
                                }
                                
                                
$tmp_c_node =& $this->createNode();
                                
$tmp_c_node->setName('mtr');
                                
$tmp_c_node->addChildArr($row_node_arr);
                                
                                
$tab_node_arr[$tmp_c_node->getId()] =& $tmp_c_node;
                            }
                            
                            
$tmp_c_node =& $this->createNode();
                            
$tmp_c_node->setName('mtable');
                            
$tmp_c_node->addChildArr($tab_node_arr);
                            
                            if (isset(
$sym['invisible'])) {
                                
$tmp_c_node->setAttr('columnalign','left');
                            }
                            
                            
$key_node_arr array_keys($node_arr);
                            
$tmp_c_node->setId($key_node_arr[0]);
                            
                            
$node_arr[$tmp_c_node->getId()] =& $tmp_c_node;
                        }
                    }
                }
            }
            
            
$this->chopExpr($sym['symlen']);
            if (!isset(
$sym['invisible'])) {
                
$node_7 =& $this->createNode();
                
$node_7->setName('mo');
                
$node_7->setContent($sym['output']);
                
$node_arr[$node_7->getId()] =& $node_7;
            }
        }
        
        return(
$node_arr);
    }
    
    function & 
parseSmplExpr()
    {
        
$sym $this->getSymbol();
        
        if (!
$sym || isset($sym['right_bracket'])) //return FALSE; 
            
return $this->emptyNode();
        
        
$this->chopExpr($sym['symlen']);
        
        
// 2005-06-11 wes: add definition type support
        
if(isset($sym['definition'])) {
            
$this->pushExpr($sym['output']);
            
$sym $this->getSymbol();
            
$this->chopExpr($sym['symlen']);
        }

        if (isset(
$sym['left_bracket'])) {
            
$node_arr =& $this->parseExpr();
            
            if (isset(
$sym['invisible'])) {
                
$node_0 =& $this->createNode();
                
$node_0->setName('mrow');
                
$node_0->addChildArr($node_arr);
                
                return(
$node_0);
            } else {
                
$node_0 =& $this->createNode();
                
$node_0->setName('mo');
                
$node_0->setContent($sym['output']);
                
                
$node_1 =& $this->createNode();
                
$node_1->setName('mrow');
                
$node_1->addChild($node_0);
                
$node_1->addChildArr($node_arr);
                
                return(
$node_1);
            }
        } elseif (isset(
$sym['unary'])) {
            
            if (
$sym['input'] == 'sqrt') {
                
$node_0 =& $this->parseSmplExpr();
                
$node_0->removeBrackets();
                
                
$node_1 =& $this->createNode();
                
$node_1->setName($sym['tag']);
                
$node_1->addChild($node_0);
                
                return(
$node_1);
            } elseif (isset(
$sym['func'])) { //added 2006-9-7 David Lippman
                
$expr ltrim($this->getCurrExpr());
                
$st $expr{0};
                
$node_0 =& $this->parseSmplExpr();
                
//$node_0->removeBrackets();
                
if ($st=='^' || $st == '_' || $st=='/' || $st=='|' || $st==',') {
                    
$node_1 =& $this->createNode();
                    
$node_1->setName($sym['tag']);
                    
$node_1->setContent($sym['output']);
                    
$this->setCurrExpr($expr);
                    return(
$node_1);
                } else {
                    
$node_1 =& $this->createNode();
                    
$node_1->setName('mrow');
                    
$node_2 =& $this->createNode();
                    
$node_2->setName($sym['tag']);
                    
$node_2->setContent($sym['output']);
                    
$node_1->addChild($node_2);
                    
$node_1->addChild($node_0);
                    return(
$node_1);
                }
            } elseif (
$sym['input'] == 'text' || $sym['input'] == 'mbox' || $sym['input'] == '"') {
                
$expr ltrim($this->getCurrExpr());
                if (
$sym['input']=='"') {
                    
$end_brckt '"';
                    
$txt substr($expr,0,strpos($expr,$end_brckt));
                } else {
                    switch(
$expr{0}) {
                        case 
'(':
                            
$end_brckt ')';
                            break;
                        case 
'[':
                            
$end_brckt ']';
                            break;
                        case 
'{':
                            
$end_brckt '}';
                            break;
                        default:
                            
$end_brckt chr(11); // A character that will never be matched.
                            
break;
                    }
                    
$txt substr($expr,1,strpos($expr,$end_brckt)-1);
                }
                
                
//$txt = substr($expr,1,strpos($expr,$end_brckt)-1);
                
$len strlen($txt);
                
                
$node_0 =& $this->createNode();
                
$node_0->setName('mrow');
                
                if (
$len 0) {
                    if (
$txt{0} == " ") {
                        
$node_1 =& $this->createNode();
                        
$node_1->setName('mspace');
                        
$node_1->setAttr('width','1ex');
                        
                        
$node_0->addChild($node_1);
                    }
                    
                    
$node_3 =& $this->createNode();
                    
$node_3->setName($sym['tag']);
                    
$node_3->setContent(trim($txt));
                    
                    
$node_0->addChild($node_3);
                    
                    if (
$len && $txt{$len-1} == " ") {
                        
$node_2 =& $this->createNode();
                        
$node_2->setName('mspace');
                        
$node_2->setAttr('width','1ex');
                        
                        
$node_0->addChild($node_2);
                    }
                    
                    
$this->chopExpr($len+2);
                }
                return(
$node_0);
                
            } elseif (isset(
$sym['acc'])) {
                
$node_0 =& $this->parseSmplExpr();
                
$node_0->removeBrackets();
                
                
$node_1 =& $this->createNode();
                
$node_1->setName($sym['tag']);
                
$node_1->addChild($node_0);
                
                
$node_2 =& $this->createNode();
                
$node_2->setName('mo');
                
$node_2->setContent($sym['output']);
                
                
$node_1->addChild($node_2);
                return(
$node_1);
            } else {
                
// Font change commands -- to complete
            
}
        } elseif (isset(
$sym['binary'])) {
            
$node_arr = array();
            
            
$node_0 =& $this->parseSmplExpr();
            
$node_0->removeBrackets();
            
            
$node_1 =& $this->parseSmplExpr();
            
$node_1->removeBrackets();
            
            
/* 2005-06-05 wes: added stackrel */
            
if ($sym['input'] == 'root' || $sym['input'] == 'stackrel') {
                
$node_arr[$node_1->getId()] =& $node_1;
                
$node_arr[$node_0->getId()] =& $node_0;
            } elseif (
$sym['input'] == 'frac') {
                
$node_arr[$node_0->getId()] =& $node_0;
                
$node_arr[$node_1->getId()] =& $node_1;
            }
            
            
$node_2 =& $this->createNode();
            
$node_2->setName($sym['tag']);
            
$node_2->addChildArr($node_arr);
            
            return(
$node_2);
        } elseif (isset(
$sym['infix'])) {
            
$node_0 =& $this->createNode();
            
$node_0->setName('mo');
            
$node_0->setContent($sym['output']);
            
            return(
$node_0);
        } elseif (isset(
$sym['space'])) {
            
$node_0 =& $this->createNode();
            
$node_0->setName('mrow');
            
            
$node_1 =& $this->createNode();
            
$node_1->setName('mspace');
            
$node_1->setAttr('width',$sym['space']);
            
            
$node_2 =& $this->createNode();
            
$node_2->setName($sym['tag']);
            
$node_2->setContent($sym['output']);
            
            
$node_3 =& $this->createNode();
            
$node_3->setName('mspace');
            
$node_3->setAttr('width',$sym['space']);
            
            
$node_0->addChild($node_1);
            
$node_0->addChild($node_2);
            
$node_0->addChild($node_3);
            
            return(
$node_0);
        } else {
            
            
// A constant
            
$node_0 =& $this->createNode();
            
$node_0->setName($sym['tag']);
            
$node_0->setContent($sym['output']);
            return(
$node_0);
        }
        
        
// Return an empty node
        
return $this->emptyNode();
    }
    
    function 
getMathML()
    {
        
$root =& $this->_node_arr[0];
        return(
$root->dumpXML());
    }
    
    function 
getCurrExpr()
    {
        return(
$this->_curr_expr);
    }
    
    function 
setCurrExpr($str)
    {
        
$this->_curr_expr $str;
    }
    
    function 
getExpr()
    {
        return(
$this->_expr);
    }
    
    function 
getPrevExpr()
    {
        return(
$this->_prev_expr);
    }
    
    function & 
createNode()
    {
        
$node =& new MathMLNode($this->_node_cntr);
        
// $node->setNamespaceAlias('m');
        
$this->_node_arr[$this->_node_cntr] =& $node;
        
$this->_node_cntr++;
        return(
$node);
    }
    
    
/**
     * Gets the largest symbol in the expression (greedy). Changed from non-greedy 26-Apr-2006
     * 
     * @parameter boolean[optional] Chop original string?
     *
     * @return mixed
     * 
     * @access private
     */
    
function getSymbol($chop_flg FALSE)
    {
        
// Implemented a reverse symbol matcher. 
        // Instead of going front to back, it goes back to front. Steven 26-Apr-2006
        
$chr_cnt strlen($this->_curr_expr);
        
        if (
$chr_cnt == 0) return FALSE;
        
        for (
$i $chr_cnt$i 0$i--) {
            
$sym_0 substr($this->_curr_expr,0,$i);
            
            
// Reading string for numeric values
            
if (is_numeric($sym_0)) {
                
                if (
$chop_flg$this->chopExpr($i);
                return array(
'input'=>$sym_0'tag'=>'mn''output'=>$sym_0'symlen'=>$i);
                
            } elseif (isset(
$this->_symbol_arr[$sym_0])) {
                
                if (
$chop_flg$this->chopExpr($i);
                
$sym_arr $this->_symbol_arr[$sym_0];
                
$sym_arr['symlen'] = $i;
                return 
$sym_arr;
            }
        }
        
        
// Reading string for alphabetic constants and the minus sign
        
$char $this->_curr_expr{0};
        
$len_left $chop_flg $this->chopExpr(1) : strlen($this->_curr_expr)-1;
        
        
// Deals with expressions of length 1
        
if ($len_left == && isset($this->_symbol_arr[$char])) {
            
$sym_arr $this->_symbol_arr[$char];
            
$sym_arr['symlen'] = 1;
            return 
$sym_arr;
        } else {
            
$tag preg_match('/[a-z]/i',$char) ? 'mi' 'mo';
            return array(
'input'=>$char'tag'=>$tag'output'=>$char'symlen'=>1);
        }
    }
    
    function 
chopExpr($strlen)
    {
        
$this->_prev_expr $this->_curr_expr;
        
        if (
$strlen == strlen($this->_curr_expr)) {
            
$this->_curr_expr '';
            return(
0);
        } else {
            
$this->_curr_expr ltrim(substr($this->_curr_expr,$strlen));
            return(
strlen($this->_curr_expr));
        }
    }    
}
?>