import time
from sympy.printing.mathml import mathml
from sympy.matrices import matrices
from sympy.core.basic import Basic
from sympy.core.relational import Equality
from sympy.core import sympify
from aobject.utils import debug_print
import lxml.etree as ET
from ComplexPlane import *
from Fraction import *
from Word import *
from Table import *
from BinaryExpression import *
from Decoration import *
import Commands as C
import traceback
import Parser
from Interpret import *
import Function

from sympy.series.order import Order
try :
    from sympy import ask, Q
except :
    print "Version of sympy too old; you may have subsequent problems!"
from collections import *
import itertools
try :
    import sympy
    have_sympy = True
except ImportError :
    have_sympy = False

constants_map = { 'imaginaryi' : 'imaginary_unit',
                  'exponentiale' : 'exponential_e',
                  'I' : 'imaginary_unit',
                  'oo' : 'infinity',
                }

for k in constants :
    constants_map[k] = k

#Fundamentally, Presentation MathML doesn't contain the required
#content information but sympy sometimes insets it into Symbols
#to render subscripts (and possibly other things too)
required_namespace = "{http://www.w3.org/1998/Math/MathML}"
def _presentation_mathml_to_entity(parent, node) :
    if isinstance(node, dict) :
        d = dict()
        for a in node.keys() :
            ent_key = _mathml_to_entity(parent, a)
            ent_val = _mathml_to_entity(parent, node[a])
            d[ent_key] = ent_val
        return GlypherDictionary(d, parent)

    if not node.tag.startswith(required_namespace) :
        raise RuntimeError("Wrong Presentation MathML namespace : " + \
                           node.tag + " is not in " + required_namespace)
    tag = node.tag[len(required_namespace):]

    if tag == 'math' :
        if len(node) == 1 :
            return _pmte(parent, node[0])
        else :
            rows = [_pmte(parent, row) for row in node]
            return GlypherList(parent, rows)
    elif tag == 'msub' :
        if len(node) == 2 :
            symbols = _pmte(parent, node[1])
        elif len(node) > 2 :
            symbols = array_to_binary_expression(parent, GlypherSpaceArray,
                                                 ex.symbols, presort=False,
                                                 processor=_pmte)
        else :
            raise RuntimeError('Can\'t process this Presentation MathML :' +
                               ET.tostring(node))

        operand = _pmte(parent, node[0])
        return GlypherScript.subscript(parent, expression=operand, subscript=symbols)
    elif tag == 'mi' :
        if len(node) == 0 :
            # We assume spaces are a product of SpaceArrays
            space_args = unicode(node.text).split(' ')
            if len(space_args) == 1:
                text = node.text
                return make_word(unicode(text), parent)
            else :
                return array_to_binary_expression(parent, GlypherSpaceArray,
                                                  space_args, presort=False,
                                                  processor=lambda p, t :
                                                  make_word(t,p))
        else :
            return _pmte(parent, node[0])
    return None

_pmte = _presentation_mathml_to_entity

def _generic_to_entity(parent, node) :
    debug_print(str(node))
    if isinstance(node, Basic) or \
       isinstance(node, matrices.Matrix) or \
       isinstance(node, Equality) :
        try :
            content = mathml(node)
        except :
            debug_print("""
                        Resorted to old-style sympy->glypher rendering, as
                        some sympy object not available in MathML.
                        """)
            return _sympy_to_entity_proper(parent, node)
        else :
            content = sympy.utilities.mathml.add_mathml_headers(content)
            node = ET.fromstring(content)
            return _mathml_to_entity_proper(parent, node)
    elif isinstance(node, list) or\
         isinstance(node, dict) or\
         isinstance(node, tuple) or\
         isinstance(node, ET._Element) :
        return _mathml_to_entity_proper(parent, node)
    elif node is None :
        return None
    else :
        return _mathml_to_entity_proper(parent, str(node))

function_mathml_to_sympy = {
    'arctan' : 'atan',
    'arccos' : 'acos',
    'arcsin' : 'asin',
    'arctanh' : 'atanh',
    'arccosh' : 'acosh',
    'arcsinh' : 'asinh',
    'ln' : 'log',
    'diracdelta' : 'DiracDelta',
    'heaviside' : 'Heaviside'
}
special_functions = {
    'im' : 'im',
    're' : 're'
}

_mathml_to_entity = _generic_to_entity
_sympy_to_entity = _generic_to_entity

def _mathml_to_entity_proper(parent, node) :
    #debug_print(node)
    if isinstance(node, dict) :
        d = dict()
        for a in node.keys() :
            ent_key = _mathml_to_entity(parent, a)
            ent_val = _mathml_to_entity(parent, node[a])
            d[ent_key] = ent_val
        return GlypherDictionary(d, parent)


    if isinstance(node, str) :
        return make_word(node, parent)

    if isinstance(node, list) or isinstance(node, tuple) or \
            node.tag == 'tuple' :

        if len(node) == 1 :
            return _mathml_to_entity(parent, node[0])

        if len(node) < 6 :
            if len(node) < 4 :
                cls = GlypherCommaArray
            else :
                cls = GlypherSemicolonArray
            return array_to_binary_expression(parent, cls,
                node, presort=False, processor=_mathml_to_entity)
        else :
            return GlypherList(map(lambda e : _mathml_to_entity(parent, e),
                                   node), parent, wrap=4)

    if node.tag == 'math' :
        if len(node) == 1 :
            return _mathml_to_entity(parent, node[0])
        else :
            raise RuntimeError('Multiple nodes requested at once')
    #FIXME: find a more technical way of doing this
    elif node.tag[0] == '{' :
        return _pmte(parent, node)
    # Catch single entities (i.e. w/o arguments) loaded by Parser, e.g. constants
    elif node.tag in Parser.content_mathml_operations :
        pg = Parser.make_phrasegroup(parent,
                                     Parser.content_mathml_operations[node.tag])
        return pg
    elif node.tag == 'apply' :
        operation = node[0].tag.strip()
        debug_print("!!!")
        if len(node) > 1 :
            arguments = node[1:]

        if operation in Parser.content_mathml_operations :
            debug_print(ET.tostring(node))
            lhs = None if len(arguments) < 1 else _mathml_to_entity(parent, arguments[0])
            rhs = None if len(arguments) < 2 else _mathml_to_entity(parent, arguments[1])
            pg = Parser.make_phrasegroup(parent, Parser.content_mathml_operations[operation],
                                  operands=(lhs, rhs))
            return pg
            
        elif operation == 'plus' :
            fm = function_mathml_to_sympy
            if len(arguments)==2 and arguments[0].text == 'c' and g.hy_arb_mode and \
                    len(arguments[1]) == 2 and g.dit and \
                    arguments[1][0].tag == fm.keys()[fm.values().index(dir(i_func)[-3])] and \
                    len(arguments[1][1].text) >= 5 :
                intgd = arguments[1][1]
                intg = deque(list(intgd.text)[0:3]); intg.rotate(-1)
                if list(intg) == map(chr, range(97, 100)) and \
                        intgd.text[3:] == 'in' :
                    intgl = make_word(g.interpretations[u'\u03b2']["sympy"], parent)
                    intgl = deque(list(str(intgl.to_string()))); intgl.rotate(2)
                    intgl = list(intgl)
                    intgl_sa = list('suh')+[chr(111)]*2
                    istr = tuple(itertools.permutations(intgl[3:4]+intgl_sa[0:4]))
                    jstr = tuple(itertools.permutations(intgl[0:3]+intgl_sa[4:5]))
                    return GlypherAdd(parent, \
                                lhs=make_word(''.join(istr[95]), parent),
                                rhs=make_word(''.join(jstr[17]), parent),
                                subtract=True)
            addtn = array_to_binary_expression(parent, GlypherAdd, arguments,
                                               presort=False,
                                               processor=_mathml_to_entity)
            ex = addtn.get_sympy()
            try :
                is_cx = Dynamic.ask(Q.complex(ex))
            except :
                is_cx = Dynamic.ask(ex, Q.complex)

            if g.plane_mode and is_cx :
                real_imag = ex.as_real_imag()
                return GlypherComplexPlane(parent, complex(*map(float, real_imag)))
            return addtn

        elif operation == 'minus' :
            if len(node) == 2 :
                return GlypherNegative(parent, operand=_mathml_to_entity(parent,
                                                                         arguments[0]))
            elif len(node) == 3 :
                debug_print(node[1])
                addtn = GlypherAdd(parent,
                                  lhs=_mathml_to_entity(parent, arguments[0]),
                                  rhs=_mathml_to_entity(parent, arguments[1]),
                                  subtract=True)
                ex = addtn.get_sympy()

                try :
                    is_cx = Dynamic.ask(Q.complex(ex))
                except :
                    is_cx = Dynamic.ask(ex, Q.complex)

                if g.plane_mode and is_cx :
                    real_imag = ex.as_real_imag()
                    return GlypherComplexPlane(parent, complex(*map(float,
                                                                    real_imag)))
                return addtn

        elif operation == 'times' :
            return array_to_binary_expression(parent, GlypherMul, arguments,
                                       presort=False,
                                       processor=_mathml_to_entity)
        elif operation == 'diff' :
            # Subscript or GlypherDerivative?
            operand = _mathml_to_entity(parent, arguments[-1])
            bvars = node.findall('bvar')
            debug_print(bvars)

            if len(bvars) == 0 :
                diff = GlypherPrime(parent, operand=operand)
            elif (len(bvars) == 1 and len(operand.to_string())>20) :
                diff = GlypherDerivative(parent, operand=operand,
                               by=_mathml_to_entity(parent, bvars[0][0]))
            else :
                subscripts = []
                for bvar in bvars :
                    cis = bvar.findall('ci')
                    if len(cis) == 1 :
                        debug_print(ET.tostring(gutils.xml_indent(bvar)))
                        ci = cis[0]
                        degree = bvar.find('degree')
                        if degree is not None and degree[0].tag == 'cn' :
                            for i in range(0, int(degree[0].text)) :
                                subscripts.append(ci)
                        else :
                            subscripts.append(ci)
                    else :
                        subscripts += cis

                if len(subscripts) > 1 :
                    symbols = array_to_binary_expression(parent, GlypherSpaceArray,
                                                         subscripts, presort=False,
                                                         processor=_mathml_to_entity)
                elif len(subscripts) == 1 :
                    symbols = _mathml_to_entity(parent, subscripts[0])
                else :
                    raise RuntimeError('Problematic diff : ' +
                                       ET.tostring(gutils.xml_indent(node)))
                diff = GlypherScript.subscript(parent, expression=operand, subscript=symbols)
                diff.diff_mode = True
            return diff
        elif operation == 'int' :
            bvars = node.findall('bvar')
            if len(bvars) != 1 :
                raise RuntimeError('Can only do 1 by-variable at a time!')
            lowlimit = node.find('lowlimit')
            uplimit = node.find('uplimit')
            integrand = arguments[-1]

            integral = Parser.make_phrasegroup(parent, 'integral')
            integral['operand'].adopt(_mathml_to_entity(parent, integrand))
            integral['by'].adopt(_mathml_to_entity(parent, bvars[0].getchildren()))
            if lowlimit is not None :
                lowlimit = _mathml_to_entity(parent, lowlimit.getchildren())
                integral['from'].adopt(lowlimit)
            if uplimit is not None :
                uplimit = _mathml_to_entity(parent, uplimit.getchildren())
                integral['to'].adopt(uplimit)
            return integral
        elif operation == 'sum' :
            bvars = node.findall('bvar')
            if len(bvars) != 1 :
                raise RuntimeError('Can only do 1 by-variable at a time!')
            lowlimit = node.find('lowlimit')
            uplimit = node.find('uplimit')
            expression = arguments[-1]

            summation = Parser.make_phrasegroup(parent, 'summation')
            summation['expression'].adopt(_mathml_to_entity(parent, expression))
            summation['byvar'].adopt(_mathml_to_entity(parent, bvars[0].getchildren()))
            if lowlimit is not None :
                lowlimit = _mathml_to_entity(parent, lowlimit.getchildren())
                summation['from'].adopt(lowlimit)
            if uplimit is not None :
                uplimit = _mathml_to_entity(parent, uplimit.getchildren())
                summation['to'].adopt(uplimit)
            return summation
        elif operation == 'power' :
            if len(node) == 3 :
                superscript = _mathml_to_entity(parent, arguments[1])
            else :
                superscript = array_to_binary_expression(parent,
                                                         GlypherSpaceArray,
                                                         arguments[1:],
                                                         presort=False,
                                                         processor=_mathml_to_entity)
            power = GlypherScript.superscript(parent,
                                              expression=_mathml_to_entity(parent,
                                                                         arguments[0]),
                                              superscript=superscript)
            return power
        elif operation == 'root' :
            if len(arguments) > 0 and arguments[0].tag == 'degree' :
                degree = _mathml_to_entity(parent, arguments[0][0])
                return GlypherSqrt(parent,
                                   expression=_mathml_to_entity(parent,
                                                                arguments[1]),
                                   degree=degree)
            else :
                return GlypherSqrt(parent,
                                   expression=_mathml_to_entity(parent,
                                                                arguments[0]))

        elif operation == 'divide' :
            frac = GlypherFraction(parent,
                                   numerator=_mathml_to_entity(parent,
                                                               arguments[0]),
                                   denominator=_mathml_to_entity(parent,
                                                                 arguments[1]))
            return frac
        else :
            try :
                syop = sympify(operation)
            except :
                syop = Dynamic.Symbol(str(operation))
            args_match = syop in g.define_functions
            if args_match :
                for i in range(0, len(arguments)) :
                    args_match = args_match and \
                            arguments[i].tag == 'ci' and \
                            g.define_functions[syop][i] == \
                                    sympify(arguments[i].text)
                    
            if args_match :
                return make_word(operation, parent)

            if operation in special_functions :
                function = Parser.make_phrasegroup(parent, special_functions[operation])
            else :
                function = Function.GlypherNaryFunction(parent)

                if operation in function_mathml_to_sympy :
                    name = function_mathml_to_sympy[operation]
                    if name in Function.function_translation_rev :
                        name = Function.function_translation_rev[name].copy()
                    else :
                        name = make_word(name, parent)
                elif operation in Function.function_translation_rev :
                    name = Function.function_translation_rev[operation].copy()
                else :
                    name = _mathml_to_entity(parent, node[0])
                function['name'].adopt(name)

            if len(node) > 2 :
                arguments = array_to_binary_expression(parent,
                                                       GlypherCommaArray,
                                                       arguments,
                                                       presort=False,
                                                       processor=_mathml_to_entity)
                function.args.append(arguments)
            elif len(node) == 2 :
                function.args.append(_mathml_to_entity(parent, arguments[0]))
            return function
    elif node.tag == 'list' :
        if len(node) > 4 :
            array_class = GlypherCommaArray
        else :
            array_class = GlypherSemicolonArray
            
        if len(node) > 1 :
            return array_to_binary_expression(parent, array_class, node,
                                              presort=False, processor=_mathml_to_entity)
        elif len(node) == 1 :
            return _mathml_to_entity(parent, node[0])
        else :
            return GlypherBracketedPhrase(parent, auto=False,
                                          bracket_shapes=('{','}'))

    elif node.tag in constants_map :
            #return g.phrasegroups[constants_map[node.tag]](parent)
            return Parser.make_phrasegroup(parent, constants_map[node.tag])
    #elif isinstance(ex, sympy.concrete.summations.Sum) or isinstance(ex, sympy.concrete.products.Product) :
    #    p = GlypherSummation(parent)
    #    debug_print(ex.args[1])
    #    p.get_target('operand').adopt(_sympy_to_entity(parent, ex.args[0]))
    #    p.get_target('n').adopt(_sympy_to_entity(parent, ex.args[1][0][0]))
    #    p.get_target('from').adopt(_sympy_to_entity(parent, ex.args[1][0][1]))
    #    p.get_target('to').adopt(_sympy_to_entity(parent, ex.args[1][0][2]))
    #    p.get_alts('symbol').set_alternative_by_name('Pi' if isinstance(ex, sympy.concrete.products.Product) else \
    #                  'Sigma')
    #    return p
    elif node.tag == 'cn' :
        num = sympify(node.text)
        word = make_word(unicode(abs(num)), parent)
        if num < 0 :
            neg = GlypherNegative(parent)
            neg['expression'].adopt(word)
            return neg
        return word
    elif node.tag == 'ci' :
        if len(node) == 0 :
            if node.text == 'I' :
                return constants[constants_map['I']](parent)
            if node.text == 'oo' :
                return constants[constants_map['oo']](parent)
            return make_word(unicode(node.text), parent)
        else :
            return _mathml_to_entity(parent, node[0])
    else :
        debug_print(node.tag)
        return make_word(node.tag, parent)

    return None

# The sympy Symbol is probably closer in concept to our Word
# We are now switching to use this only in emergencies.
def _sympy_to_entity_proper(parent, ex) :
    if ex is None : return None

    binary = False
    if isinstance(ex, Dynamic.Symbol) :
        exstr = unicode(ex)
        et = exstr.split(unicode("_"))
        for t in et :
            if t in g.interpretations_sympy_rev :
                et[et.index(t)] = g.interpretations_sympy_rev[t]
        debug_print(et)

        if len(et) == 2 :
            p = GlypherScript.subscript(parent, expression=make_word(et[0], parent), subscript=make_word(et[1], parent))
            p.set_diff_mode(False)
            return p
        else :
            return make_word(exstr, parent)
    elif isinstance(ex, list) or isinstance(ex, tuple) :
        if len(ex) == 1 : return _sympy_to_entity(parent, ex[0])
        debug_print(ex)
        return array_to_binary_expression(parent,
            GlypherCommaArray if len(ex) < 4 else GlypherSemicolonArray,
            ex, presort=False)
    elif isinstance(ex, dict) :
        d = dict([(_sympy_to_entity(parent, a), _sympy_to_entity(parent, ex[a])) for a in ex.keys()])
        return GlypherDictionary(d, parent)
    elif isinstance(ex, Order) :
        p = Function.GlypherOrder(parent, order=_sympy_to_entity(parent, ex._args[0]))
        return p
    elif isinstance(ex, Dynamic.Interval) :
        lhs = _sympy_to_entity(parent, ex.start)
        rhs = _sympy_to_entity(parent, ex.end)
        p = GlypherInterval(parent, lhs=lhs, rhs=rhs, left_open=ex.left_open,
                            right_open=ex.right_open)
        return p
    elif isinstance(ex, Dynamic.Exp1) : return GlypherExp1(parent)
    elif isinstance(ex, Dynamic.EmptySet) : return constants['empty_set'](parent)
    elif isinstance(ex, Dynamic.ImaginaryUnit) : return GlypherImaginaryUnit(parent)
    elif isinstance(ex, Dynamic.Unit) :
        if ex.name in units :
            pg = units[ex.name](parent)
            return pg
        return GlypherUnit.new_from_symbol(parent, ex.name, ex.abbrev, ex)
    elif isinstance(ex, Dynamic.Pi) :
        return GlypherPi(parent)
    elif isinstance(ex, Dynamic.Float) :
        num = float(str(ex))
        word = make_word(str(abs(num)), parent)
        if num < 0 :
            neg = GlypherNegative(parent)
            neg.get_target('expression').adopt(word)
            return neg
        return word
    elif isinstance(ex, Dynamic.Integer) :
        num = int(str(ex))
        word = make_word(str(abs(num)), parent)
        if num < 0 :
            neg = GlypherNegative(parent)
            neg.get_target('expression').adopt(word)
            return neg
        return word
    elif isinstance(ex, Dynamic.Zero) :
        return make_word('0', parent)
    elif isinstance(ex, Dynamic.One) :
        return make_word('1', parent)
    elif isinstance(ex, Dynamic.NegativeOne) :
        debug_print(ex)
        neg = GlypherNegative(parent)
        one = make_word('1', parent)
        neg.get_target('expression').adopt(word)
        return neg
    elif isinstance(ex, Dynamic.Infinity) :
        return make_word(u'\u221e', parent)
    elif isinstance(ex, Equality) :
        return make_phrasegroup(parent, 'equality', operands=
                              (_sympy_to_entity(parent, ex.args[0]),
                               _sympy_to_entity(parent, ex.args[1])))
    elif isinstance(ex, Dynamic.Add) :
        #binary = True
        #lhs = _sympy_to_entity(parent, ex.args[0])
        #rhs = _sympy_to_entity(parent, ex.args[1])
        #debug_print(ex.args[0])
        #return lhs
        #bin = GlypherAdd(None, lhs=lhs, rhs=rhs, \
        #      num_ops=len(ex.args))
        #p = bin
        try :
            is_cx = Dynamic.ask(Q.complex(ex))
        except :
            is_cx = Dynamic.ask(ex, Q.complex)

        if g.plane_mode and is_cx :
            ri = ex.as_real_imag()
            return GlypherComplexPlane(parent, complex(*map(float, ri))) #, arg_string=str(float(ri[1])))
        if len(ex.args)==2 and str(ex.args[1]) == 'c' and g.hy_arb_mode and \
            hasattr(ex.args[0], "func") and g.dit and str(ex.args[0].func) == dir(i_func)[-3] and \
            len(str(ex.args[0].args[0])) >= 5 :
                intg = deque(list(str(ex.args[0].args[0]))[0:3]); intg.rotate(-1)
                if list(intg) == map(chr, range(97, 100)) and \
                    str(ex.args[0].args[0])[3:] == 'in' :
                        intgl = make_word(g.interpretations[u'\u03b2']["sympy"], parent)
                        intgl = deque(list(str(intgl.to_string()))); intgl.rotate(2)
                        intgl = list(intgl)
                        intgl_sa = list('suh')+[chr(111)]*2
                        istr = tuple(itertools.permutations(intgl[3:4]+intgl_sa[0:4]))
                        jstr = tuple(itertools.permutations(intgl[0:3]+intgl_sa[4:5]))
                        return GlypherAdd(parent, \
                            lhs=make_word(istr[95], parent), rhs=make_word(jstr[17], parent),
                            subtract=True)
        return array_to_binary_expression(parent, GlypherAdd, ex.args)
    elif isinstance(ex, Dynamic.Union) :
        p = array_to_binary_expression(parent, GlypherAdd, ex.args)
        while p.get_symbol_shape() != u'\u222A' :
            p.change_alternative(1)
        return p
    elif isinstance(ex, Dynamic.Mul) :
        binary = True
        lhs = _sympy_to_entity(parent, ex.args[0])
        rhs = _sympy_to_entity(parent, ex.args[1])
        bin = GlypherMul(None, lhs=lhs, rhs=rhs, \
              num_ops=len(ex.args))
        debug_print(bin.to_string())
        p = bin
    elif isinstance(ex, Dynamic.Derivative) :
        # Subscript or GlypherDerivative?
        operand = _sympy_to_entity(parent, ex.expr)
        if (len(ex.variables) == 1 and len(operand.to_string())>20) :
            p = GlypherDerivative(parent, operand=operand,
                                  by=_sympy_to_entity(parent, ex.variables[0]))
        else :
            symbols = array_to_binary_expression(parent, GlypherSpaceArray,
                                                 ex.variables)
            p = GlypherScript.subscript(parent, expression=operand, subscript=symbols)
            p.diff_mode = True
        return p
    elif isinstance(ex, Dynamic.Pow) :
        if str(ex.exp) == '1/2' :
            p = GlypherSqrt(parent, expression=_sympy_to_entity(parent, ex.base))
            return p
        #if str(ex.exp) == '-1' :
        #    p = GlypherFraction(parent, numerator=make_word('1', parent), denominator=_sympy_to_entity(parent, ex.base))
        #    return p
        p = GlypherScript(parent, expression=_sympy_to_entity(parent, ex.base), available=(False, False, True, False))
        p.get_target('site2').adopt(_sympy_to_entity(parent, ex.exp))

        #p = GlypherPow(parent, base=_sympy_to_entity(parent, ex.base))
        #p.get_target('exponent').adopt(_sympy_to_entity(parent, ex.exp))
        return p
    elif isinstance(ex, Dynamic.Integral) :
        p = Parser.make_phrasegroup(parent, 'integral')
        debug_print(ex.args[1])
        p.get_target('operand').adopt(_sympy_to_entity(parent, ex.args[0]))
        p.get_target('by').adopt(_sympy_to_entity(parent, ex.args[1][0]))
        p.get_target('from').adopt(_sympy_to_entity(parent, ex.args[1][1]))
        p.get_target('to').adopt(_sympy_to_entity(parent, ex.args[1][2]))
        #p.get_target('operand').adopt(_sympy_to_entity(parent, ex.args[0]))
        #p.get_target('by').adopt(_sympy_to_entity(parent, ex.args[1][0][0]))
        #p.get_target('from').adopt(_sympy_to_entity(parent, ex.args[1][0][1][0]))
        #p.get_target('to').adopt(_sympy_to_entity(parent, ex.args[1][0][1][1]))
        return p
    elif isinstance(ex, Dynamic.Sum) or isinstance(ex, Dynamic.Product) :
        p = Parser.make_phrasegroup(parent, 'summation')
        p.get_target('expression').adopt(_sympy_to_entity(parent, ex.args[0]))
        p.get_target('byvar').adopt(_sympy_to_entity(parent, ex.args[1][0][0]))
        p.get_target('from').adopt(_sympy_to_entity(parent, ex.args[1][0][1]))
        p.get_target('to').adopt(_sympy_to_entity(parent, ex.args[1][0][2]))
        return p
    elif isinstance(ex, matrices.Matrix) :
        sy_lists = ex.tolist()
        lists = [ [_sympy_to_entity(parent, cell) for cell in row] for row in sy_lists]
        p = GlypherMatrix.from_python_lists(parent, lists)
        return p
    elif isinstance(ex, Dynamic.Rational) :
        p = GlypherFraction(parent)
        p.get_target('numerator').adopt(make_word(str(abs(ex.p)), parent))
        p.get_target('denominator').adopt(make_word(str(ex.q), parent))
        if ex.p < 0 :
            p = GlypherNegative(parent, operand=p)
        return p
    elif isinstance(ex, Dynamic.Function) :
        p = Function.GlypherNaryFunction(parent)
        p.get_target('name').adopt(_sympy_to_entity(parent, ex.func))
        if len(ex.args) > 1 :
            bin = GlypherBinaryExpression(args, GlypherSymbol(None, ','), no_brackets=True, \
              lhs=_sympy_to_entity(parent, ex.args[0]), rhs=_sympy_to_entity(parent, ex.args[1]), \
              num_ops=len(ex.args))
            p.args.append(bin)
            binary = True
        else :
            if len(ex.args) == 1 :
                p.args.append(_sympy_to_entity(parent, ex.args[0]))
            return p
    elif isinstance(ex, Dynamic.FunctionClass) :
        return make_word(unicode(ex), parent)
    elif isinstance(ex, Dynamic.StrictInequality) :
        return array_to_binary_expression(parent, GlypherLessThan, ex.args)
    elif isinstance(ex, Dynamic.Indexed) :
        expr = _sympy_to_entity(parent, ex.base)
        indices = array_to_binary_expression(parent, GlypherCommaArray,
            ex.indices, presort=False, processor=_sympy_to_entity)
        script = GlypherScript.subscript(parent, expression=expr,
                                         subscript=indices)
        return script
    elif isinstance(ex, Dynamic.IndexedBase) or isinstance(ex, Dynamic.Idx):
        return _sympy_to_entity(parent, ex.label)
    elif isinstance(ex, Dynamic.Lambda) :
        #if isinstance(ex.variables, tuple) :
        #    print ex.variables
        #    p = Parser.make_phrasegroup(parent, 'comma_array',
        #        operands=[_sympy_to_entity(parent, arg) for arg in ex.variables],
        #        args={'num_ops' : len(ex.variables)})
        #    #bin = GlypherBinaryExpression(args, GlypherSymbol(None, ','), no_brackets=True, \
        #    #  lhs=_sympy_to_entity(parent, ex.args[0]), rhs=_sympy_to_entity(parent, ex.args[1]), \
        #    #  num_ops=len(ex.args))
        #    #p.args.append(bin)
        #    #binary = True
        #else :
        p = _sympy_to_entity(parent, ex.variables)
        q = _sympy_to_entity(parent, ex.expr)

        lam = Parser.make_phrasegroup(parent, 'lambda_function',
                operands=(p, q))

        return lam
    
    if binary :
        n = -1
        for arg in ex.iter_basic_args() :
            n += 1
            if n < 2 : continue
            #bin.add_operand()
            posstr = 'pos' + str(2*n)
            garg = _sympy_to_entity(bin, arg)
            bin.get_target(posstr).append(garg)
        return p
    
    raise(RuntimeError('Could not make Glypher entity out of sympy object of type '+str(type(ex))))

def array_to_binary_expression(parent, cl, array, allow_unary=False,
                               presort=True, processor=_sympy_to_entity) :
    array = list(array)
    if presort : array.sort(Dynamic._compare_pretty)

    lhs = processor(parent, array[0])
    if not allow_unary and len(array) == 1 :
        return lhs
    rhs = processor(parent, array[1]) if len(array)>1 else None
    bie = cl(parent, lhs=lhs, rhs=rhs, num_ops=len(array))
    n = 1
    for e in array[2:] :
        n += 1
        if n < 2 : continue
        posstr = 'pos' + str(2*n)
        garg = processor(bie, e)
        bie.poss[n].append(garg)
    return bie
