# HG changeset patch
# User Nils Bruin <nbruin@sfu.ca>
# Date 1333516952 25200
# Node ID 80c2c1306fa5d15dbb92be8a6227095d64092090
# Parent  a7b7a32c3cb90fccaec47dcfbf3b0cdfe33525cb
Ensure that FDerivativeOperator can be evaluated on general arguments
and ensure that translations to/from maxima are done properly

diff --git a/sage/calculus/calculus.py b/sage/calculus/calculus.py
--- a/sage/calculus/calculus.py
+++ b/sage/calculus/calculus.py
@@ -1332,7 +1332,7 @@ def inverse_laplace(ex, t, s):
 ###################################################################
 # symbolic evaluation "at" a point
 ###################################################################
-def at(ex, **kwds):
+def at(ex, *args, **kwds):
     """
     Parses ``at`` formulations from other systems, such as Maxima.
     Replaces evaluation 'at' a point with substitution method of
@@ -1373,6 +1373,13 @@ def at(ex, **kwds):
     """
     if not isinstance(ex, (Expression, Function)):
         ex = SR(ex)
+    if len(args) == 1 and isinstance(args[0],list):
+        for c in args[0]:
+            kwds[str(c.lhs())]=c.rhs()
+    else:
+        if len(args) !=0:
+            raise TypeError,"at can take at most one argument, which must be a list"
+                
     return ex.subs(**kwds)
 
 
diff --git a/sage/interfaces/maxima_abstract.py b/sage/interfaces/maxima_abstract.py
--- a/sage/interfaces/maxima_abstract.py
+++ b/sage/interfaces/maxima_abstract.py
@@ -1753,13 +1753,10 @@ class MaximaAbstractElement(InterfaceEle
 
             sage: y,d = var('y,d')
             sage: f = function('f')
-            sage: latex(maxima(derivative(f(x*y*d), d,x,x,y)))
-            Traceback (most recent call last):
-            ...
-            NotImplementedError: arguments must be distinct variables
+            sage: latex(maxima(derivative(f(x*y), x)))
+            \left(\left.{{{\it \partial}}\over{{\it \partial}\,{\it t_0}}}\,f  \left({\it t_0}\right)\right|_{\left[ {\it t_0}=x\,y \right] }  \right)\,y
             sage: latex(maxima(derivative(f(x,y,d), d,x,x,y)))
             {{{\it \partial}^4}\over{{\it \partial}\,d\,{\it \partial}\,x^2\,  {\it \partial}\,y}}\,f\left(x , y , d\right)
-
             sage: latex(maxima(d/(d-2)))
             {{d}\over{d-2}}
         """
diff --git a/sage/interfaces/maxima_lib.py b/sage/interfaces/maxima_lib.py
--- a/sage/interfaces/maxima_lib.py
+++ b/sage/interfaces/maxima_lib.py
@@ -67,7 +67,7 @@ which is anyway set to raise an error::
 #                  http://www.gnu.org/licenses/
 #*****************************************************************************
 
-from sage.symbolic.ring import SR
+from sage.symbolic.ring import SR, var
 
 from sage.libs.ecl import *
 
@@ -212,6 +212,7 @@ max_plus=EclObject("$PLUS")
 max_minus=EclObject("$MINUS")
 max_use_grobner=EclObject("$USE_GROBNER")
 max_to_poly_solve=EclObject("$TO_POLY_SOLVE")
+max_at=EclObject("%AT")
 
 def stdout_to_string(s):
     r"""
@@ -1007,7 +1008,7 @@ sage_op_dict = {
     sage.functions.log.log : "%LOG",
     sage.functions.other.factorial : "MFACTORIAL",
     sage.functions.other.erf : "%ERF",
-    sage.functions.other.gamma_inc : "%GAMMA_INCOMPLETE"
+    sage.functions.other.gamma_inc : "%GAMMA_INCOMPLETE",
 }
 #we compile the dictionary
 sage_op_dict = dict([(k,EclObject(sage_op_dict[k])) for k in sage_op_dict])
@@ -1180,6 +1181,60 @@ def mdiff_to_sage(expr):
     """
     return max_to_sr(expr.cadr()).diff(*[max_to_sr(e) for e in expr.cddr()])
 
+def mlist_to_sage(expr):
+    r"""
+    Special conversion rule for MLIST expressions.
+    
+    INPUT:
+    
+    - ``expr`` - ECL object; a Maxima MLIST expression (i.e., a list)
+
+    OUTPUT: a python list of converted expressions.
+    
+    EXAMPLES::
+    
+        sage: from sage.interfaces.maxima_lib import maxima_lib, mlist_to_sage
+        sage: L=maxima_lib("[1,2,3]")
+        sage: L.ecl()
+        <ECL: ((MLIST SIMP) 1 2 3)>
+        sage: mlist_to_sage(L.ecl())
+        [1, 2, 3]
+    """
+    return [max_to_sr(x) for x in expr.cdr()]
+
+def max_at_to_sage(expr):
+    r"""
+    Special conversion rule for AT expressions.
+    
+    INPUT:
+    
+    - ``expr`` - ECL object; a Maxima AT expression
+    
+    OUTPUT: symbolic expression
+    
+    EXAMPLES::
+    
+        sage: from sage.interfaces.maxima_lib import maxima_lib, max_at_to_sage
+        sage: a=maxima_lib("'at(f(x,y,z),[x=1,y=2,z=3])") 
+        sage: a
+        'at(f(x,y,z),[x=1,y=2,z=3])
+        sage: max_at_to_sage(a.ecl())
+        f(1, 2, 3)
+        sage: a=maxima_lib("'at(f(x,y,z),x=1)")
+        sage: a
+        'at(f(x,y,z),x=1)
+        sage: max_at_to_sage(a.ecl())
+        f(1, y, z)
+    """
+    arg=max_to_sr(expr.cadr())
+    subsarg=caddr(expr)
+    if caar(subsarg)==mlist:
+        subsvalues=dict( (v.lhs(),v.rhs()) for v in max_to_sr(subsarg))
+    else:
+        v=max_to_sr(subsarg)
+        subsvalues=dict([(v.lhs(),v.rhs())])
+    return SR(arg).subs(subsvalues)
+
 def dummy_integrate(expr):
     r"""
     We would like to simply tie Maxima's integrate to
@@ -1220,7 +1275,9 @@ special_max_to_sage={
     mrat : mrat_to_sage,
     mqapply : mqapply_to_sage,
     mdiff : mdiff_to_sage,
-    EclObject("%INTEGRATE") : dummy_integrate
+    EclObject("%INTEGRATE") : dummy_integrate,
+    max_at : max_at_to_sage,
+    mlist : mlist_to_sage
 }
 
 special_sage_to_max={
@@ -1310,7 +1367,15 @@ def sr_to_max(expr):
             args = expr.operands()
             if (not all(is_SymbolicVariable(v) for v in args) or
                 len(args) != len(set(args))):
-                raise NotImplementedError, "arguments must be distinct variables"
+                temp_args=[var("t%s"%i) for i in range(len(args))]
+                f =sr_to_max(op.function()(*temp_args))
+                params = op.parameter_set()
+                deriv_max = [[mdiff],f]
+                for i in set(params):
+                    deriv_max.extend([sr_to_max(temp_args[i]), EclObject(params.count(i))])
+                at_eval=sr_to_max([temp_args[i]==args[i] for i in range(len(args))])
+                return EclObject([[max_at],deriv_max,at_eval])
+                
             f = sr_to_max(op.function()(*args))
             params = op.parameter_set()
             deriv_max = []
diff --git a/sage/misc/parser.pyx b/sage/misc/parser.pyx
--- a/sage/misc/parser.pyx
+++ b/sage/misc/parser.pyx
@@ -946,6 +946,9 @@ cdef class Parser:
             name = tokens.last_token_string()
             tokens.next()
             return name, self.p_expr(tokens)
+        if token == "[" :
+            tokens.backtrack()
+            return self.p_list(tokens)
         else:
             tokens.backtrack()
             return self.p_expr(tokens)
diff --git a/sage/symbolic/expression_conversions.py b/sage/symbolic/expression_conversions.py
--- a/sage/symbolic/expression_conversions.py
+++ b/sage/symbolic/expression_conversions.py
@@ -16,7 +16,7 @@ overridden by subclasses.
 ###############################################################################
 
 import operator as _operator
-from sage.symbolic.ring import SR
+from sage.symbolic.ring import SR,var
 from sage.symbolic.pynac import I
 from sage.functions.all import exp
 from sage.symbolic.operators import arithmetic_operators, relation_operators, FDerivativeOperator
@@ -474,15 +474,16 @@ class InterfaceInit(Converter):
             sage: print m.derivative(b, b.operator())
             diff('f(x), x, 2)
 
-        We can only convert to Maxima derivatives if the corresponding operand
-        of the function is a variable::
+        We can also convert expressions where the argument is not just a
+        variable, but the result is an "at" expression using temporary
+        variables::
 
             sage: y = var('y')
-            sage: t = f(x*y).diff(x)
+            sage: t = (f(x*y).diff(x))/y
+            sage:  t
+            D[0](f)(x*y)
             sage: m.derivative(t, t.operator())
-            Traceback (most recent call last):
-            ...
-            NotImplementedError: arguments must be distinct variables
+            "at(diff('f(t0), t0, 1), [t0 = x*y])"
         """        
         #This code should probably be moved into the interface
         #object in a nice way.
@@ -492,7 +493,15 @@ class InterfaceInit(Converter):
         args = ex.operands()
         if (not all(is_SymbolicVariable(v) for v in args) or
             len(args) != len(set(args))):
-            raise NotImplementedError, "arguments must be distinct variables"
+            temp_args=[var("t%s"%i) for i in range(len(args))]
+            f = operator.function()
+            params = operator.parameter_set()
+            params = ["%s, %s"%(temp_args[i], params.count(i)) for i in set(params)]
+            subs = ["%s = %s"%(t,a) for t,a in zip(temp_args,args)]
+            return "at(diff('%s(%s), %s), [%s])"%(f.name(),
+                ", ".join(map(repr,temp_args)),
+                ", ".join(params),
+                ", ".join(subs))
 
         f = operator.function()
         params = operator.parameter_set()
diff --git a/sage/symbolic/operators.py b/sage/symbolic/operators.py
--- a/sage/symbolic/operators.py
+++ b/sage/symbolic/operators.py
@@ -1,5 +1,5 @@
 import operator
-from sage.symbolic.ring import is_SymbolicVariable
+from sage.symbolic.ring import is_SymbolicVariable, var
 
 arithmetic_operators = {operator.add: '+',
                         operator.sub: '-',
@@ -38,10 +38,14 @@ class FDerivativeOperator(object):
             sage: op = FDerivativeOperator(f, [0,1])
             sage: op(x,y)
             D[0, 1](foo)(x, y)
+            sage: op(x,x^2)
+            D[0, 1](foo)(x, x^2)
         """
         if (not all(is_SymbolicVariable(x) for x in args) or
-            len(args) != len(set(args))):
-            raise NotImplementedError, "currently all arguments must be distinct variables"
+                len(args) != len(set(args))):
+            temp_args=[var("t%s"%i) for i in range(len(args))]
+            vars=[temp_args[i] for i in self._parameter_set]
+            return self._f(*temp_args).diff(*vars).function(*temp_args)(*args)
         vars = [args[i] for i in self._parameter_set]
         return self._f(*args).diff(*vars)
 
