File:  [CENS] / python / pyGiNaC / wrappers3 / ex_py.cpp
Revision 1.10: download - view: text, annotated - select for diffs - revision graph
Thu May 24 15:03:40 2001 UTC (16 years, 6 months ago) by pearu
Branches: MAIN
CVS tags: HEAD
Added subs(ls,lr,no_pattern) support

/*
# This file is part of the PyGiNaC package.
# http://cens.ioc.ee/projects/pyginac/
#
# $Revision: 1.10 $
# $Id: ex_py.cpp,v 1.10 2001-05-24 15:03:40 pearu Exp $
#
# Copyright 2001 Pearu Peterson all rights reserved,
# Pearu Peterson <pearu@cens.ioc.ee>
# Permission to use, modify, and distribute this software is given under the
# terms of the LGPL.  See http://www.fsf.org
#
# NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
#
*/

/*DT
  Ex
  --
  >>> from ginac import ex,symbol,numeric

  To create ex object from Python objects:
  >>> ex()
  numeric('0')
  >>> ex(symbol('a'))
  symbol('a')
  >>> ex(3)
  numeric('3')
  >>> ex('3/4')
  numeric('3/4')
  >>> ex([])
  lst([])
  >>> ex([2,'5/4',symbol('g'),ex()])
  lst([numeric('2'), numeric('5/4'), symbol('g'), numeric('0')])
  >>> ex([[]])
  lst([lst([])])
  >>> ex([1,[symbol('c')]])
  lst([numeric('1'), lst([symbol('c')])])
  >>> ex({})
  Traceback (most recent call last):
  ...
  TypeError: ex_from_ref() argument must be number|string|ex|sequence (got <type 'dictionary'>)
  
  Swap expressions:
  >>> from ginac import swap
  >>> a,b = symbol('a'),numeric('2/3')
  >>> a.swap(b)
  >>> a
  numeric('2/3')
  >>> b
  symbol('a')
  >>> a.swap(2)
  Traceback (most recent call last):
  ...
  TypeError: int
  >>> swap(a,b)
  >>> a,b
  (symbol('a'), numeric('2/3'))
  >>> swap(2,a)
  Traceback (most recent call last):
  ...
  TypeError: int

  Operations with expressions:
  >>> +b
  numeric('2/3')
  >>> -b
  numeric('-2/3')
  >>> a+b
  add([symbol('a'), numeric('2/3')])
  >>> a+1
  add([symbol('a'), numeric('1')])
  >>> b+2
  numeric('8/3')
  >>> 2+b
  numeric('8/3')
  >>> a+a
  mul([symbol('a'), numeric('2')])
  >>> a-a
  numeric('0')
  >>> a*a
  power(symbol('a'), numeric('2'))
  >>> a**5
  power(symbol('a'), numeric('5'))

  Expanding expressions:
  >>> from ginac import expand
  >>> e=(a+b)**2
  >>> e
  power(add([symbol('a'), numeric('2/3')]), numeric('2'))
  >>> e.expand()
  add([power(symbol('a'), numeric('2')), mul([symbol('a'), numeric('4/3')]), numeric('4/9')])
  >>> e.expand(1)
  add([power(symbol('a'), numeric('2')), mul([symbol('a'), numeric('4/3')]), numeric('4/9')])
  >>> expand(e)
  add([power(symbol('a'), numeric('2')), mul([symbol('a'), numeric('4/3')]), numeric('4/9')])

  Pretty-printing:
  >>> print a+b
  2/3 + a
  >>> print a*b
  2/3 * a
  >>> print a**b
  a ** (2/3)
  >>> print a-b
  -2/3 + a
  >>> print b-a
  2/3 - a
  >>> print -a
  -a
  >>> print (1+a) / a
  (1 + a) / a

  Accesing operants/members in ex objects:
  >>> from ginac import nops,op
  >>> e
  power(add([symbol('a'), numeric('2/3')]), numeric('2'))
  >>> e.nops(), nops(e)
  (2, 2)
  >>> e.op(0), op(e,0)
  (add([symbol('a'), numeric('2/3')]), add([symbol('a'), numeric('2/3')]))
  >>> e.op(1), op(e,1)
  (numeric('2'), numeric('2'))
  >>> e.op(-1)
  Traceback (most recent call last):
  ...
  IndexError: ex.op(-1) index out of range ([0...1])
  >>> e.op(2)
  Traceback (most recent call last):
  ...
  IndexError: ex.op(2) index out of range ([0...1])
  >>> a.nops()
  0
  >>> a.op(0)
  Traceback (most recent call last):
  ...
  IndexError: ex.op(0) index out of range ([0...-1])

  Information about operants/members:
  >>> from ginac import has
  >>> e.has(a), has(e,a)
  (1, 1)
  >>> e.has(symbol()), has(e,symbol())
  (0, 0)
  >>> has(e,e)
  1

  Polynomial stuff:
  >>> from ginac import degree,ldegree,coeff,lcoeff,tcoeff
  >>> x,y=symbol('x'),symbol('y')
  >>> p = 2*x**2 + x*y
  >>> print p
  y * x + 2 * x ** 2
  >>> p.ldegree(x),p.degree(x)
  (1, 2)
  >>> p.ldegree(y),p.degree(y)
  (0, 1)
  >>> ldegree(p,x),degree(p,x)
  (1, 2)
  >>> print p.coeff(x,0)
  0
  >>> print p.coeff(x,1)
  y
  >>> print p.coeff(x,2)
  2
  >>> print p.coeff(x,10)
  0
  >>> print p.lcoeff(x)
  2
  >>> print p.tcoeff(x)
  y
  >>> print lcoeff(p, x), tcoeff(p, x)
  2 y
  >>> print coeff(p,x), coeff(p,y)
  y x
  >>> from ginac import numer,denom
  >>> print p/x**2
  (y * x + 2 * x ** 2) / x ** 2
  >>> print numer(p/x**2), denom(p/x**2)
  y + 2 * x x
  >>> print (p/x**2).numer(), (p/x**2).denom()
  y + 2 * x x
  >>> r = -2*p/3+x
  >>> print r.unit(x)
  -1
  >>> print r.content(x)
  1
  >>> print r.primpart(x)
  2/3 * y * x + 4/3 * x ** 2 - x
  >>> print r.primpart(x,1)
  2/3 * y * x + 4/3 * x ** 2 - x
  >>> print (30*r).integer_content()
  10

  Functions for rational functions:
  >>> from ginac import normal
  >>> print normal(x+1/x)
  (1 + x ** 2) / x
  >>> print normal(x+1/x,2)
  x ** (-1) + x
  >>> from ginac import lst,sin,to_rational
  >>> l = lst()
  >>> e = x+1/sin(x+1)
  >>> print e.to_rational(l)
  symbol8 ** (-1) + x
  >>> l
  lst([relational(symbol('symbol8'), sin(add([symbol('x'), numeric('1')])))])
  >>> print l
  [symbol8 == sin(1 + x)]
  >>> l = lst()
  >>> print to_rational(sin(x+1)+1/sin(x+1),l)
  symbol9 + symbol9 ** (-1)
  >>> print l
  [symbol9 == sin(1 + x)]
  >>> from ginac import collect
  >>> print collect(2*x-x*sin(x),x)
  x * (2 - sin(x))
  >>> print (2*x-x*sin(x)).collect(x)
  x * (2 - sin(x))
  
  Evaluation of expressions:
  >>> from ginac import eval,evalf
  >>> e = sin(2*x/3)/3
  >>> print e
  1/3 * sin(2/3 * x)
  >>> print e.evalf()
  0.33333333333333333334 * sin(0.6666666666666666667 * x)
  >>> print evalf(e)
  0.33333333333333333334 * sin(0.6666666666666666667 * x)
  >>> print e.evalf(2)
  0.33333333333333333334 * sin(2/3 * x)
  >>> print evalf(e,2)
  0.33333333333333333334 * sin(2/3 * x)

  Differentiation:
  >>> from ginac import diff
  >>> e = (sin(2*x)+x**2)/2
  >>> print e.diff(x)
  cos(2 * x) + x
  >>> print diff(e,x)
  cos(2 * x) + x
  >>> print diff(e,x,3)
  -4 * cos(2 * x)
  >>> diff(e,1+x)
  Traceback (most recent call last):
  ...
  TypeError: symbol from_python() argument must be (string|ex(symbol)) (got ex(add))

  Power series:
  >>> from ginac import series
  >>> print sin(x).series((x,0),5)
  1 * x + (-1/6) * x ** 3 + Order(x ** 5)
  >>> print series(sin(x)/x,(x,0),4)
  1 + (-1/6) * x ** 2 + Order(x ** 3)

  Substitution:
  >>> from ginac import subs
  >>> print sin(x).subs(x,x**2)
  sin(x ** 2)
  >>> print subs(sin(x),x,x**2)
  sin(x ** 2)
  >>> y = symbol('y')
  >>> print subs(sin(x+y),[x,y],[x**2,x*y])
  sin(y * x + x ** 2)
  
  Auxiliary functions:
  >>> from ginac import lhs,rhs
  >>> rhs(x)
  Traceback (most recent call last):
  ...
  TypeError: ex.rhs() can be used only for ex(relational).
  >>> lhs(x)
  Traceback (most recent call last):
  ...
  TypeError: ex.lhs() can be used only for ex(relational).
  >>> x.rhs()
  Traceback (most recent call last):
  ...
  TypeError: ex.rhs() can be used only for ex(relational).
  >>> (1+x).is_equal(x+1)
  1
  >>> (1+x).is_equal(x-1)
  0
  >>> (1+x).is_equal({})
  0
  >>> from ginac import is_zero
  >>> is_zero(x), x.is_zero()
  (0, 0)
  >>> is_zero(x-x), (x-x).is_zero()
  (1, 1)
  >>> is_zero(0),is_zero(0.0),is_zero(34),is_zero({})
  (1, 1, 0, 0)
  >>> print numer(3), denom(3)
  3 1
  >>> print numer(3.3), denom(3.3)
  3.2999999999999998224 1

*/

#include "cln_py.hpp"
#include "ex_py.hpp"
#include "python_str.hpp"
#include "python_repr.hpp"
#include "python_csrc.hpp"

using namespace pyginac;

PyObject * ex_py_class = NULL;
PyObject * pyginac_dict = NULL;

bool return_false(py::ref obj) { return false; }
bool is_zero_1(py::ref obj);
GiNaC::ex numer_1(py::ref obj);
GiNaC::ex denom_1(py::ref obj);
GiNaC::ex subs_1(py::ref expr,py::ref e);
GiNaC::ex subs_2(py::ref expr,const GiNaC::lst & ls, const GiNaC::lst & rs);
GiNaC::ex subs_3(py::ref expr,const GiNaC::lst & ls, const GiNaC::lst & rs, bool no_pattern);
GiNaC::ex collect_2(py::ref expr, const GiNaC::lst & syms);
GiNaC::ex collect_3(py::ref expr, const GiNaC::lst & syms, bool distributed);
bool Sequence_Check(PyObject * o);

#define PYGINAC_TOPYTHON1(NAME) PyObject * to_python(const GiNaC::NAME & obj);
#define PYGINAC_TOPYTHON2(NAME) \
 PyObject * to_python(const GiNaC::NAME & obj) {\
  return to_python(GiNaC::ex(obj));\
}
BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE
PYGINAC_TOPYTHON1(numeric);
PYGINAC_TOPYTHON1(symbol);
PYGINAC_TOPYTHON1(relational);
PYGINAC_TOPYTHON1(constant);
PYGINAC_TOPYTHON1(matrix);
PYGINAC_TOPYTHON1(lst);
PYGINAC_TOPYTHON1(add);
PYGINAC_TOPYTHON1(mul);
PYGINAC_TOPYTHON1(ncmul);
BOOST_PYTHON_END_CONVERSION_NAMESPACE

#define PYGINAC_FROMPYTHON_TYPEERROR(NAME,EXPECTED) { \
  std::ostrstream os; \
  os << #NAME " from_python() argument must be " #EXPECTED " (got "; \
  if (ExInstance_Check(o)) \
    os << "ex(" << from_python(o, py::type<const GiNaC::ex &>()).bp->class_name() << ")"; \
  else \
    os << PyString_AsString(PyObject_Repr(PyObject_Type(o))); \
  os << ")" << std::ends; \
  PyErr_SetString(PyExc_TypeError, os.str()); \
  throw py::error_already_set(); \
}

#define PYGINAC_PROTOS
#include "ex_py_subs.cpp"

BOOST_PYTHON_MODULE_INIT(_ginac)
{
  try
    {
      py::module_builder this_module("_ginac");
      pyginac_dict = PyModule_GetDict(this_module.module());
      
      py::class_builder<ex> ex_w_class(this_module, "_ex_w");
      py::class_builder<GiNaC::ex, ex> ex_class(this_module, "ex");

      ex_class.declare_base(ex_w_class);
      ex_py_class = py::as_object(ex_class.get_extension_class());

      ex_class.def(py::constructor<>());
      ex_class.def(py::constructor<const GiNaC::ex &>());
      ex_class.def(py::constructor<py::ref>());
      ex_class.def(&ex::python_repr, "__repr__");
      ex_class.def(&ex::python_str, "__str__");
      ex_class.def(&ex::print_csrc, "to_csrc");
      ex_class.def(&ex::print_latex, "to_latex");

      ex_class.def(&ex::coerce, "__coerce__");
      ex_class.def(&ex::op_pos, "__pos__");
      ex_class.def(&ex::op_neg, "__neg__");
      ex_class.def(&ex::op_add, "__add__");
      ex_class.def(&ex::op_sub, "__sub__");
      ex_class.def(&ex::op_mul, "__mul__");
      ex_class.def(&ex::op_div, "__div__");
      ex_class.def(&ex::op_pow, "__pow__");
      ex_class.def(&ex::op_lshift, "__lshift__");
      ex_class.def(&ex::swap, "swap");
      this_module.def(&ex::swap, "swap");
      ex_class.def((GiNaC::ex (GiNaC::ex::*)(void))&ex::expand, "expand");
      ex_class.def((GiNaC::ex (GiNaC::ex::*)(unsigned))&ex::expand, "expand");
      this_module.def((GiNaC::ex (GiNaC::ex::*)(void))&ex::expand, "expand");
      this_module.def((GiNaC::ex (GiNaC::ex::*)(unsigned))&ex::expand, "expand");
      ex_class.def(&ex::has, "has");
      this_module.def(&ex::has, "has");
      ex_class.def(&ex::nops_0, "nops");
      this_module.def(&ex::nops_0, "nops");
      ex_class.def(&ex::op_1, "op");
      this_module.def(&ex::op_1, "op");
      ex_class.def(&ex::degree, "degree");
      this_module.def(&ex::degree, "degree");
      ex_class.def(&ex::ldegree, "ldegree");
      this_module.def(&ex::ldegree, "ldegree");
      ex_class.def(&ex::coeff_1, "coeff");
      ex_class.def((GiNaC::ex (GiNaC::ex::*)(const GiNaC::ex &, int))&ex::coeff, "coeff");
      this_module.def(&ex::coeff_1, "coeff");
      this_module.def((GiNaC::ex (GiNaC::ex::*)(const GiNaC::ex &, int))&ex::coeff, "coeff");
      ex_class.def(&ex::lcoeff, "lcoeff");
      this_module.def(&ex::lcoeff, "lcoeff");
      ex_class.def(&ex::tcoeff, "tcoeff");
      this_module.def(&ex::tcoeff, "tcoeff");
      ex_class.def(&ex::numer, "numer");
      this_module.def(&numer_1, "numer");
      ex_class.def(&ex::denom, "denom");
      this_module.def(&denom_1, "denom");
	  
      ex_class.def(&ex::unit, "unit");
      ex_class.def(&ex::content, "content");
      ex_class.def(&ex::integer_content, "integer_content");
      ex_class.def((GiNaC::ex (GiNaC::ex::*)(const GiNaC::symbol &) const)&ex::primpart, "primpart");
      ex_class.def(&ex::primpart_2, "primpart");

      ex_class.def(&ex::normal_0, "normal");
      this_module.def(&ex::normal_0, "normal");
      ex_class.def((GiNaC::ex (GiNaC::ex::*)(int))&ex::normal, "normal");
      this_module.def((GiNaC::ex (GiNaC::ex::*)(int))&ex::normal, "normal");
      ex_class.def(&ex::to_rational, "to_rational");
      this_module.def(&ex::to_rational, "to_rational");
      ex_class.def(&collect_2, "collect");
      this_module.def(&collect_2, "collect");
      ex_class.def(&collect_3, "collect");
      this_module.def(&collect_3, "collect");

      ex_class.def(&ex::eval_0, "eval");
      ex_class.def((GiNaC::ex (GiNaC::ex::*)(int))&ex::eval, "eval");
      ex_class.def(&ex::evalf_0, "evalf");
      ex_class.def((GiNaC::ex (GiNaC::ex::*)(int))&ex::evalf, "evalf");
      this_module.def(&ex::eval_0, "eval");
      this_module.def((GiNaC::ex (GiNaC::ex::*)(int))&ex::eval, "eval");
      this_module.def(&ex::evalf_0, "evalf");
      this_module.def((GiNaC::ex (GiNaC::ex::*)(int))&ex::evalf, "evalf");

      ex_class.def(&ex::diff_1, "diff");
      ex_class.def((GiNaC::ex (GiNaC::ex::*)(const GiNaC::symbol &, unsigned) const)&ex::diff, "diff");
      this_module.def(&ex::diff_1, "diff");
      this_module.def((GiNaC::ex (GiNaC::ex::*)(const GiNaC::symbol &, unsigned) const)&ex::diff, "diff");
      ex_class.def(&ex::series_2, "series");
      ex_class.def(&ex::series_3, "series");
      this_module.def(&ex::series_2, "series");
      this_module.def(&ex::series_3, "series");

      ex_class.def(&ex::subs_1, "subs");
	  ex_class.def(&ex::subs_2, "subs");
	  ex_class.def(&ex::subs_3, "subs");
      //ex_class.def((GiNaC::ex (GiNaC::ex::*)(const GiNaC::lst &, const GiNaC::lst &) const)&ex::subs, "subs");
      this_module.def(&subs_1, "subs");
      this_module.def(&subs_2, "subs");
	  this_module.def(&subs_3, "subs");
      //this_module.def((GiNaC::ex (GiNaC::ex::*)(const GiNaC::lst &, const GiNaC::lst &) const)&ex::subs, "subs");
      ex_class.def(&ex::lhs_0, "lhs");
      ex_class.def(&ex::rhs_0, "rhs");
      this_module.def(&ex::lhs_0, "lhs");
      this_module.def(&ex::rhs_0, "rhs");

      ex_class.def(&ex::is_equal_1, "is_equal");
      ex_class.def(&ex::is_zero, "is_zero");
      this_module.def(&is_zero_1, "is_zero");

      ex_class.def(&ex::getitem, "__getitem__");
      ex_class.def(&ex::setitem, "__setitem__");
      ex_class.def(&ex::delitem, "__delitem__");
      ex_class.def(&ex::len, "__len__");

      ex_class.def(&ex::get_class_name, "get_class_name");
      ex_class.def(&ex::gethash, "get_hash");
      ex_class.def(&ex::get_function, "get_function");
      ex_class.def(&ex::get_precedence, "get_precedence");
      
#define PYGINAC_DEFS
#include "ex_py_subs.cpp"
    }
  catch(...)
    {
      py::handle_exception();
    }
}

ex::~ex() {}


BOOST_PYTHON_BEGIN_CONVERSION_NAMESPACE
PYGINAC_TOPYTHON2(numeric);
PYGINAC_TOPYTHON2(symbol);
PYGINAC_TOPYTHON2(relational);
PYGINAC_TOPYTHON2(constant);
PYGINAC_TOPYTHON2(matrix);
PYGINAC_TOPYTHON2(lst);
PYGINAC_TOPYTHON2(add);
PYGINAC_TOPYTHON2(mul);
PYGINAC_TOPYTHON2(ncmul);
bool Sequence_Check(PyObject * o) {
  if (ExInstance_Check(o)) {
    GiNaC::ex e = from_python(o, py::type<const GiNaC::ex &>());
    if (is_ex_exactly_of_type(e, matrix)
	|| is_ex_exactly_of_type(e, lst)
	)
      return true;
  } else if (PySequence_Check(o))
    return true;
  return false;
}
BOOST_PYTHON_END_CONVERSION_NAMESPACE

#include "ex_py_subs.cpp"

/*M_DT__init__(self,*args)
  ex() - return numeric('0')
  ex(obj) - if n is ...
    ... Python number or string, return `number(obj)'.
    ... sequence, return `lst(obj)'. ex() function is applied to
        all items of the sequence `obj'.
  ex(ex(...)) -> ex(...)
*/

GiNaC::ex pyginac::ex_from_ref(py::ref obj, bool flag=true) {
  PyObject * o = obj.get();
  if (ExInstance_Check(o))
    return BOOST_PYTHON_CONVERSION::from_python(o, py::type<const GiNaC::ex &>());
  if (Number_Check(o) || PyString_Check(o))
    return BOOST_PYTHON_CONVERSION::from_python(o, py::type<const GiNaC::numeric &>());
  if (PySequence_Check(o))
    return BOOST_PYTHON_CONVERSION::from_python(o, py::type<const GiNaC::lst &>());
  if (flag) {
    std::ostrstream os;
    os << "ex_from_ref() argument must be number|string|ex|sequence (got ";
    os << PyString_AsString(PyObject_Repr(PyObject_Type(o))) << ")" << std::ends;
    PyErr_SetString(PyExc_TypeError, os.str());
    throw py::error_already_set();
  }
  return GiNaC::numeric(-1);
}

/*M_DT __coerce__(self, other)
  Return tuple (self, ex(other)).
 */
py::tuple ex::coerce(py::ref other) const {
  //cerr << "coerce:" << std::endl;
  //cerr << "this=" << *this << std::endl;
  //cerr << "other=" << PyString_AsString(PyObject_Repr(other.get())) << std::endl;  
  if (is_ex_exactly_of_type(*this, matrix) && Sequence_Check(other.get()))
    return py::tuple((const GiNaC::ex &)(*this), GiNaC::ex(from_python(other.get(), py::type<const GiNaC::matrix &>())));
  return py::tuple((const GiNaC::ex &)(*this), ex_from_ref(other));
}
/*TODO Document the behaviour of +,-,*,/,** operations with various
  ex objects.
 */
/*M_DT __pos__(self)
 */
GiNaC::ex ex::op_pos(void) const {
  return *this;
}
/*M_DT __neg__(self)
 */
GiNaC::ex ex::op_neg(void) const {
  switch (this->bp->tinfo()) {
  case GiNaC::TINFO_matrix:
    return ex_to_matrix(*this).mul(GiNaC::numeric(-1));
  case GiNaC::TINFO_pseries:
    return ex_to_pseries(*this).mul_const(GiNaC::numeric(-1));
  }
  return -(*this);
}

/*M_DT __add__(self, other)
 */
/*TODO ex.op_add(pseries,other.series()) */
GiNaC::ex ex::op_add(py::ref other) const {
  GiNaC::ex o = ex_from_ref(other);
  switch (this->bp->tinfo()) {
  case GiNaC::TINFO_matrix:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_matrix: return ex_to_matrix(*this).add(ex_to_matrix(o));
    }
    break;
  case GiNaC::TINFO_numeric:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_numeric: return ex_to_numeric(*this).add(ex_to_numeric(o));
    }
    break;
  case GiNaC::TINFO_pseries:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_pseries: return ex_to_pseries(*this).add_series(ex_to_pseries(o));
    }
    break;
  }
  return *this + o;
}
/*M_DT __sub__(self, other)
 */
/*TODO ex.op_sub(pseries,other.series()) */
GiNaC::ex ex::op_sub(py::ref other) const {
  GiNaC::ex o = ex_from_ref(other);
  switch (this->bp->tinfo()) {
  case GiNaC::TINFO_matrix:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_matrix: return ex_to_matrix(*this).sub(ex_to_matrix(o));
    }
    break;
  case GiNaC::TINFO_numeric:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_numeric: return ex_to_numeric(*this).sub(ex_to_numeric(o));
    }
    break;
  case GiNaC::TINFO_pseries:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_pseries:
      GiNaC::ex no = ex_to_pseries(o).mul_const(GiNaC::numeric(-1));
      if (no.bp->tinfo() == GiNaC::TINFO_pseries)
	return ex_to_pseries(*this).add_series(ex_to_pseries(no));
      return *this + no;
    }
    break;
  }
  return *this - o;
}
/*M_DT __mul__(self, other)
 */
/*TODO Fix op_mul(pseries,other) if self.is_terminating()*/
GiNaC::ex ex::op_mul(py::ref other) const {
  GiNaC::ex o = ex_from_ref(other);
  switch (this->bp->tinfo()) {
  case GiNaC::TINFO_matrix:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_matrix:
      //cerr << "op_mul:" << std::endl;
      //cerr << "this=" << *this << std::endl;
      //cerr << "other=" << o << std::endl;
#if PY_MAJOR_VERSION < 2 || (PY_MAJOR_VERSION==2 && PY_MINOR_VERSION==0)
      return ex_to_matrix(o).mul(ex_to_matrix(*this));
#else
      return ex_to_matrix(*this).mul(ex_to_matrix(o));
#endif
    case GiNaC::TINFO_numeric: return ex_to_matrix(*this).mul(ex_to_numeric(o));
    }
    break;
  case GiNaC::TINFO_numeric:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_matrix: return ex_to_matrix(o).mul(ex_to_numeric(*this));
    case GiNaC::TINFO_numeric: return ex_to_numeric(*this).mul(ex_to_numeric(o));
    case GiNaC::TINFO_pseries: return ex_to_pseries(o).mul_const(ex_to_numeric(*this));
    }
    break;
  case GiNaC::TINFO_pseries:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_numeric: return ex_to_pseries(*this).mul_const(ex_to_numeric(o));
    case GiNaC::TINFO_pseries: return ex_to_pseries(*this).mul_series(ex_to_pseries(o));
    default:
      GiNaC::pseries s = ex_to_pseries(*this);
      GiNaC::ex os = o.series(GiNaC::ex(GiNaC::relational(s.get_var(),
							  s.get_point())),
			      s.degree(s.get_var())+1);
      if (is_ex_exactly_of_type(os, pseries))
	return s.mul_series(ex_to_pseries(os));
    }
    break;
  default:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_pseries:
      GiNaC::pseries s = ex_to_pseries(o);
      GiNaC::ex os = this->series(GiNaC::ex(GiNaC::relational(s.get_var(),
							      s.get_point())),
				  s.degree(s.get_var())+1);
      if (is_ex_exactly_of_type(os, pseries))
	return ex_to_pseries(os).mul_series(s);
    }
  }
  return *this * o;
}
/*M_DT __div__(self, other)
 */
/*TODO ex.op_div(pseries,other.series()) */
GiNaC::ex ex::op_div(py::ref other) const {
  GiNaC::ex o = ex_from_ref(other);
  switch (this->bp->tinfo()) {
  case GiNaC::TINFO_matrix:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_matrix: return ex_to_matrix(*this).mul(ex_to_matrix(o).inverse());
    case GiNaC::TINFO_numeric: return ex_to_matrix(*this).mul(ex_to_numeric(o).inverse());
    }
    break;
  case GiNaC::TINFO_numeric:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_matrix: return ex_to_matrix(o).inverse().mul(ex_to_numeric(*this));
    case GiNaC::TINFO_numeric: return ex_to_numeric(*this).div(ex_to_numeric(o));
    }
    break;
 case GiNaC::TINFO_pseries:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_numeric: return ex_to_pseries(*this).mul_const(ex_to_numeric(o).inverse());
    }
    break;
  }
  return *this / o;
}
/*M_DT __pow__(self, other, m = None)
 */
GiNaC::ex ex::op_pow(py::ref other, py::ref m) const {
  GiNaC::ex o = ex_from_ref(other);
  switch (this->bp->tinfo()) {
  case GiNaC::TINFO_matrix:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_numeric:
      /*TODO Impl. ex.pow(matrix, numeric)*/
      //GiNaC::numeric n = ex_to_numeric(o);
      //if (n.is_pos_integer())
      //return GiNaC::ncpower(*(this->bp), n.to_int());
      break;
    }
    break;
  case GiNaC::TINFO_numeric:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_numeric: return ex_to_numeric(*this).power(ex_to_numeric(o));
    }
    break;
  case GiNaC::TINFO_pseries:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_numeric:
      PyObject *mo = m.get();
      GiNaC::numeric no = ex_to_numeric(o);
      GiNaC::pseries s = ex_to_pseries(*this);
      if (mo == Py_None) {
	if (no.is_integer())
	  return s.power_const(no,no.to_int()*s.degree(s.get_var()));
      } else {
	GiNaC::ex me = ex_from_ref(m);
	if (is_ex_exactly_of_type(me, numeric)) {
	  GiNaC::numeric n = ex_to_numeric(me);
	if (n.is_integer())
	  return s.power_const(no, n.to_int());
	}
      }
      break;
    }
    break;
  }
  return GiNaC::power(*this, o);
}
/*M_DT __lshift__(self,other)
  ex(pseries)<<int(other) - Return a new pseries object with the powers
    shifted by degree `other'.
 */
GiNaC::ex ex::op_lshift(py::ref other) const {
  GiNaC::ex o = ex_from_ref(other);
  switch (this->bp->tinfo()) {
  case GiNaC::TINFO_pseries:
    switch (o.bp->tinfo()) {
    case GiNaC::TINFO_numeric:
      GiNaC::numeric n = ex_to_numeric(o);
      if (n.is_integer())
	return ex_to_pseries(*this).shift_exponents(n.to_int());
      break;
    }
    break;
  }
  std::ostrstream os;
  os << "ex.lshift() is available for (ex(pseries),int) (got (ex(";
  os << this->bp->class_name() << "),ex("<<o.bp->class_name()<<")))"<<std::ends;
  PyErr_SetString(PyExc_TypeError, os.str());
  throw py::error_already_set();  
}

/*M_DT swap(self,other)
  Swap efficiently the contents of two expressions.
  `other' must be an ex object.
*/
/*F_DT swap(expr,other)
  Swap efficiently the contents of two expressions.
  Both `expr' and `other' must be ex objects.
*/

/*TODO expose ex.info()
 */

/*M_DT expand(self,options=0)
  Expand expression.
  options:
    1 - expand trigonometric,
    2 - expand indexed.
*/
/*F_DT expand(expr,options=0)
  Expand expression `expr'.
  `expr' must be an ex object.
  options:
    1 - expand trigonometric,
    2 - expand indexed.
*/

/*M_DT has(self,other)
  Check if object contains any occurrences of `other'.
  `other' must be an ex object.
*/
/*F_DT has(expr,other)
  Check if `expr' contains any occurrences of `other'.
  Both `expr' and `other' must be ex objects.
*/

/*M_DT nops(self)
  Return number of operands/members.
*/
/*F_DT nops(expr)
  Return number of operands/members in `expr'.
  expr must be an ex object.
*/
unsigned ex::nops_0(void) const {
  unsigned n = this->nops();
  if (n==0) return n;
  if (is_ex_exactly_of_type(*this,function)
      && is_ex_exactly_of_type(this->op(n-1),pyfunc))
    return n-1;
  return n;
}

/*M_DT op(self,i)
  Return operand/member at position `i'.
*/
/*F_DT op(expr,i)
  Return `expr' operand/member at position `i'.
  expr must be an ex object.
*/
GiNaC::ex ex::op_1 (long i) const {
  if ((unsigned)i >= this->nops_0()) {
	std::ostrstream os;
	os << "ex.op("<<i<<") index out of range ([0..."<<(long)(this->nops_0())-1<<"])" << std::ends;
	PyErr_SetString(PyExc_IndexError, os.str());
	throw py::error_already_set();
  }
  return this->op(i);
}

/*M_DT degree(self, sym)
  Return degree of highest power in expression `sym'.
  `sym' must be an ex object.
*/
/*M_DT ldegree(self, sym)
  Return degree of lowest power in expression `sym'.
  `sym' must be an ex object.
*/
/*F_DT degree(expr,sym)
  Return degree of highest power in expression `sym' of `expr'.
  Both `expr' and `sym' must be ex objects.
*/
/*F_DT ldegree(expr,sym)
  Return degree of lowest power in expression `sym' of `expr'.
  Both `expr' and `sym' must be ex objects.
*/
/*M_DT coeff(self, sym, n = 1)
  Return coefficient of degree `n' in expressin `sym'.
  `sym' must be an ex object.
*/
/*F_DT coeff(expr, sym, n = 1)
  Return coefficient of degree `n' in expressin `sym' of `expr'.
  Both `expr' and `sym' must be ex objects.
*/
GiNaC::ex ex::coeff_1(const GiNaC::ex & sym) const { return this->coeff(sym); }
/*M_DT lcoeff(self, sym)
  Return `self.coeff(sym, self.degree(sym))'.
*/
/*F_DT lcoeff(expr, sym)
  Return `coeff(expr, sym, degree(expr, sym))'.
*/
/*M_DT tcoeff(self, sym)
  Return `self.coeff(sym, self.ldegree(sym))'.
*/
/*F_DT tcoeff(expr, sym)
  Return `coeff(expr, sym, ldegree(expr, sym))'.
*/
/*M_DT numer(self)
  Return numerator of an expression.
  If the expression is not of the normal form "numerator/denominator",
  it is first converted to this form and then the numerator is returned.
*/
/*F_DT numer(expr)
  Return numerator of an expression.
  If the expression is not of the normal form "numerator/denominator",
  it is first converted to this form and then the numerator is
  returned.  `expr' must be an ex object.
*/
GiNaC::ex numer_1(py::ref obj) {
  return ex_from_ref(obj).numer();
}
/*M_DT denom(self)
  Return denominator of an expression.
  If the expression is not of the normal form "numerator/denominator",
  it is first converted to this form and then the denominator is returned.
*/
/*F_DT denom(expr)
  Return denominator of an expression.
  If the expression is not of the normal form "numerator/denominator",
  it is first converted to this form and then the denominator is returned.
  `expr' must be an ex object.
*/
GiNaC::ex denom_1(py::ref obj) {
  return ex_from_ref(obj).denom();
}
/*M_DT unit(self, sym)  
  Compute unit part (= sign of leading coefficient) of a multivariate
  polynomial in Z[x].  The product of unit part, content part, and
  primitive part is the polynomial itself.
  `sym' must be an ex(symbol) object.
*/
/*M_DT content(self, sym)
  Compute content part (= unit normal GCD of all coefficients) of a
  multivariate polynomial in Z[x].  The product of unit part, content
  part, and primitive part is the polynomial itself.
  `sym' must be an ex(symbol) object.
*/
/*M_DT primpart(self, sym, cont=None)
  Compute primitive part (using the content part `cont' for efficiency
  if given) of a multivariate polynomial in Z[x].  The product of unit
  part, content part, and primitive part is the polynomial itself.
  `sym' must be an ex(symbol) object.
*/
/*M_DT integer_content(self)
  Compute the integer content (= GCD of all numeric coefficients) of
  an expanded polynomial.
*/
GiNaC::ex ex::primpart_2(const GiNaC::symbol & sym, py::ref cont) const {
  return this->primpart(sym, ex_from_ref(cont));
}

/*M_DT normal(self,level=0)
  Normalization of rational functions.

  This function converts an expression to its normal form
  "numerator/denominator", where numerator and denominator are
  (relatively prime) polynomials. Any subexpressions which are not
  rational functions (like non-rational numbers, non-integer powers or
  functions like sin(), cos() etc.) are replaced by temporary symbols
  which are re-substituted by the (normalized) subexpressions before
  normal() returns (this way, any expression can be treated as a
  rational function). normal() is applied recursively to arguments of
  functions etc.
  level - maximum depth of recursion.
*/
/*F_DT normal(expr,level=0)
  Normalization of rational functions.

  This function converts an expression to its normal form
  "numerator/denominator", where numerator and denominator are
  (relatively prime) polynomials. Any subexpressions which are not
  rational functions (like non-rational numbers, non-integer powers or
  functions like sin(), cos() etc.) are replaced by temporary symbols
  which are re-substituted by the (normalized) subexpressions before
  normal() returns (this way, any expression can be treated as a
  rational function). normal() is applied recursively to arguments of
  functions etc.
  level - maximum depth of recursion.
*/
GiNaC::ex ex::normal_0(void) const {
  return this->normal();
}
/*M_DT to_rational(self, repl_lst)
  Rationalization of non-rational functions.

  This function converts a general expression to a rational polynomial
  by replacing all non-rational subexpressions (like non-rational
  numbers, non-integer powers or functions like sin(), cos() etc.) to
  temporary symbols. This makes it possible to use functions like
  gcd() and divide() on non-rational functions by applying
  to_rational() on the arguments, calling the desired function and
  re-substituting the temporary symbols in the result. To make the
  last step possible, all temporary symbols and their associated
  expressions are collected in the list specified by the `repl_lst'
  parameter in the form {symbol == expression}, ready to be passed as
  an argument to ex.subs().

  `repl_lst' must be an ex(lst) object. It collects a GiNaC list of all
  temporary symbols and their replacements.  */
/*F_DT to_rational(expr, repl_lst)
  Rationalization of non-rational functions.

  This function converts a general expression `expr' to a rational
  polynomial by replacing all non-rational subexpressions (like
  non-rational numbers, non-integer powers or functions like sin(),
  cos() etc.) to temporary symbols. This makes it possible to use
  functions like gcd() and divide() on non-rational functions by
  applying to_rational() on the arguments, calling the desired
  function and re-substituting the temporary symbols in the result. To
  make the last step possible, all temporary symbols and their
  associated expressions are collected in the list specified by the
  `repl_lst' parameter in the form {symbol == expression}, ready to be
  passed as an argument to ex.subs().

  `expr' must be an ex object. `repl_lst' must be an ex(lst) object.
  It collects a GiNaC list of all temporary symbols and their replacements.  */

/*M_DT collect(self, syms, distributed = 0)
  Sort expression in terms of powers of `syms'.
*/
/*F_DT collect(expr, syms, distributed = 0)
  Sort expression `expr' in terms of powers of `syms'.
*/
GiNaC::ex collect_2(py::ref expr, const GiNaC::lst & syms) {
  return ex_from_ref(expr).collect(syms);
}
GiNaC::ex collect_3(py::ref expr, const GiNaC::lst & syms, bool distributed) {
  return ex_from_ref(expr).collect(syms, distributed);
}

/*M_DT eval(self, level=0)
  Perform automatic non-interruptive symbolic evaluation on expression.
*/
/*M_DT evalf(self, level=0)
  Evaluate object numerically.
*/
/*F_DT eval(expr, level=0)
  Perform automatic non-interruptive symbolic evaluation on `expr'.
  `expr' must be an ex object.
*/
/*F_DT evalf(expr, level=0)
  Evaluate `expr' numerically.
  `expr' must be an ex object.
*/
GiNaC::ex ex::eval_0(void) const {
  return this->eval();
}
GiNaC::ex ex::evalf_0(void) const {
  return this->evalf();
}
/*M_DT diff(self, sym, nth=1)
  Compute partial derivative of an expression.
  `sym' must be an ex(symbol) object.
*/
/*F_DT diff(expr, sym, nth=1)
  Compute partial derivative of an expression `expr'.
  `expr' must be an ex object.
  `sym' must be an ex(symbol) object.
 */
GiNaC::ex ex::diff_1(const GiNaC::symbol & sym) const {
  return this->diff(sym);
}
/*M_DT series(self, rel, order, options=0)
  Compute the truncated series expansion of an expression.
  Return an expression containing an object ex(pseries) to
  represent the series. If the series does not terminate within the
  given truncation order, the last term of the series will be an order
  term.
  rel - expansion relation, lhs holds variable and rhs holds point.
  order - truncation order of series calculations.
  options = 1 -  suppress branchcut.
  `rel' must be an ex(relational) object or a 2-sequence.
*/
/*F_DT series(expr, rel, order, options=0)
  Compute the truncated series expansion of an expression `expr'.
  Return an expression containing an object ex(pseries) to
  represent the series. If the series does not terminate within the
  given truncation order, the last term of the series will be an order
  term.
  rel - expansion relation, lhs holds variable and rhs holds point.
  order - truncation order of series calculations.
  options = 1 -  suppress branchcut.
  `expr' must be an ex object.
  `rel' must be an ex(relational) object or a 2-sequence.
*/
GiNaC::ex ex::series_2(const GiNaC::relational & rel, int order) const {
return this->series_3(rel,order,0);
}
GiNaC::ex ex::series_3(const GiNaC::relational & rel, int order, unsigned options) const {
  return this->series(rel, order, options);
}
/*M_DT subs(self, *args)
  Substitute objects in an expression (syntactic substitution) and
  return the result as a new expression (that is already evaluated).

  self.subs(e) - `e' must be ex(relational)|ex(lst(relational,...)).
  self.subs(ls, lr, no_pattern = 0) - `ls',`lr' are ex(lst)|ex
*/
/*F_DT subs(expr, *args)
  Substitute objects in an expression `expr' (syntactic substitution)
  and return the result as a new expression (that is already evaluated).

  subs(expr, e) - `e' must be ex(relational)|ex(lst(relational,...)).
  subs(expr, ls, lr, no_pattern=0) - `ls, lr' are lists of symbols and their
    replacements, respectively.
*/
GiNaC::ex ex::subs_1(py::ref e) const {
  GiNaC::ex r = ex_from_ref(e);
  if (is_ex_exactly_of_type(r, relational)
      || is_ex_exactly_of_type(r, lst))
    return this->subs(r);
  PyErr_SetString(PyExc_TypeError, "ex.subs() argument must be an ex(relational)|ex(lst) object.");
  throw py::error_already_set();
}
GiNaC::ex ex::subs_2(const GiNaC::lst & ls, const GiNaC::lst & rs) {
  return this->subs(ls, rs);
}
GiNaC::ex ex::subs_3(const GiNaC::lst & ls, const GiNaC::lst & rs, bool no_pattern) {
  return this->subs(ls, rs, no_pattern);
}

GiNaC::ex subs_1(py::ref expr, py::ref e) {
  GiNaC::ex r = ex_from_ref(e);
  if (is_ex_exactly_of_type(r, relational)
      || is_ex_exactly_of_type(r, lst))
    return ex_from_ref(expr).subs(r);
  PyErr_SetString(PyExc_TypeError, "subs() second argument must be an ex(relational)|ex(lst) object.");
  throw py::error_already_set();
}
GiNaC::ex subs_2(py::ref expr,const GiNaC::lst & ls, const GiNaC::lst & rs) {
  return ex_from_ref(expr).subs(ls, rs);
}
GiNaC::ex subs_3(py::ref expr,const GiNaC::lst & ls, const GiNaC::lst & rs, bool no_pattern) {
  return ex_from_ref(expr).subs(ls, rs, no_pattern);
}

/*M_DT lhs(self)
  Left hand side of relational expression.
*/
/*M_DT rhs(self)
  Right hand side of relational expression.
*/
/*F_DT lhs(expr)
  Left hand side of relational expression `expr'.
*/
/*F_DT rhs(expr)
  Right hand side of relational expression `expr'.
*/
GiNaC::ex ex::lhs_0(void) const {
  if (is_ex_exactly_of_type(*this, relational))
    return this->lhs();
  PyErr_SetString(PyExc_TypeError, "ex.lhs() can be used only for ex(relational).");
  throw py::error_already_set();
}
GiNaC::ex ex::rhs_0(void) const {
  if (is_ex_exactly_of_type(*this, relational))
    return this->rhs();
  PyErr_SetString(PyExc_TypeError, "ex.rhs() can be used only for ex(relational).");
  throw py::error_already_set();
}
/*M_DT is_equal(self, obj)
  Test for equality.
  This is only a quick test, meaning objects should be in the same
  domain. You might have to .expand(), .normal() objects first,
  depending on the domain of your computation, to get a more reliable
  answer.
*/
bool ex::is_equal_1(py::ref obj) const {
  return this->is_equal(ex_from_ref(obj, false));
}
/*M_DT is_zero(self)
  Test for zero.
*/
/*F_DT is_zero(expr)
  Test for zero.
  `expr' must be an ex object.
 */
bool is_zero_1(py::ref obj) {
  return ex_from_ref(obj,false).is_zero();
}

/*M_DT__str__(self)
  Return pretty-print string representation of an ex object.
*/
std::string ex::python_str(void) const
{
  std::ostrstream os;
  GiNaC::python_str(os, GiNaC::ex(*this));
  os << std::ends;
  return os.str();
}

/*M_DT__repr__(self)
  Return reproducible string representation of an ex object.
*/
std::string ex::python_repr(void) const
{
  std::ostrstream os;
  GiNaC::python_repr(os, GiNaC::ex(*this));
  os << std::ends;
  return os.str();
}
/*M_DT to_csrc(self)
 */
std::string ex::print_csrc(void) const
{
  std::ostrstream os;
  GiNaC::python_csrc(os, GiNaC::ex(*this));
  //this->print(GiNaC::print_csrc(os));
  os << std::ends;
  return os.str();
}
/*M_DT to_latex(self)
 */
std::string ex::print_latex(void) const
{
  std::ostrstream os;
  this->print(GiNaC::print_latex(os));
  os << std::ends;
  return os.str();
}

/*M_DT __getitem__(self,index)
 */
PyObject* ex::getitem(py::ref index) const {
  if (is_ex_exactly_of_type(*this, matrix))
    return matrix_getitem(ex_to_matrix(*this), index); /* See matrix_py.cpp */
  return basic_getitem(*(this->bp), index); /* See lst_py.cpp */
}

/*M_DT __setitem__(self,index,other)
 */
void ex::setitem(py::ref index, py::ref other) {
  if (is_ex_exactly_of_type(*this, matrix))
    matrix_setitem((GiNaC::matrix &)(*(this->bp)), index, other); /* See matrix_py.cpp */
  else
    basic_setitem(*(this->bp), index, other); /* See lst_py.cpp */
}
/*M_DT __delitem__(self,index)
  `self' must be ex(lst) [not implemented].
 */
void ex::delitem(py::ref index) {
  if (0 && is_ex_exactly_of_type(*this, lst)) /*TODO del lst */
    lst_delitem((GiNaC::lst &)(*(this->bp)), index);
  else {
    std::ostrstream os;
    os << "ex.delitem() for " << this->bp->class_name() << std::ends;
    PyErr_SetString(PyExc_NotImplementedError, os.str());
    throw py::error_already_set();
  }
}
/*M_DT __len__(self)
  For ex(matrix), return number of rows.
  For ex(numeric|symbol|constant), raise NotImplementedError.
  For other ex objects, return nops().
 */
unsigned ex::len(void) const {
  if (is_ex_exactly_of_type(*this, matrix))
    return ex_to_matrix(*this).rows();
  if (!(is_ex_exactly_of_type(*this, numeric)
      || is_ex_exactly_of_type(*this, symbol)
      || is_ex_exactly_of_type(*this, constant))
      )
    return this->nops_0();
  std::ostrstream os;
  os << "ex.len() for " << this->bp->class_name() << std::ends;
  PyErr_SetString(PyExc_NotImplementedError, os.str());
  throw py::error_already_set(); 
}
/*M_DT get_class_name(self)
  Return GiNaC class name of an ex object.
 */
std::string ex::get_class_name(void) const {
  std::ostrstream os;
  os << this->bp->class_name() << std::ends;
  return os.str(); 
}
/*M_DT get_hash(self)
  Return a GiNaC hash value of the object. 
*/
/*M_DT get_precedence(self)
  Get relative precedence level (useful for implementing pretty-printed output).
*/
unsigned ex::get_precedence(void) const {
  return this->bp->precedence();
}

PyObject * ex::get_function(void) const {
  std::string name;
  if (is_function())
    name = get_name();
  else
    name = get_class_name();
  PyObject * item = PyDict_GetItemString(pyginac_dict,(char *)name.c_str());
  if (item!=NULL) {
    Py_INCREF(item);
    return item;
  }
  std::ostrstream os;
  os << "ex.get_function() for " << name << std::ends;
  PyErr_SetString(PyExc_NotImplementedError, os.str());
  throw py::error_already_set();  
}


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>