/[escript]/branches/more_shared_ptrs_from_1812/escript/src/Data.h
ViewVC logotype

Diff of /branches/more_shared_ptrs_from_1812/escript/src/Data.h

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1092 by gross, Fri Apr 13 03:39:49 2007 UTC revision 1748 by ksteube, Wed Sep 3 06:10:39 2008 UTC
# Line 1  Line 1 
1  // $Id$  
2  /*  /* $Id$ */
3   ************************************************************  
4   *          Copyright 2006 by ACcESS MNRF                   *  /*******************************************************
5   *                                                          *   *
6   *              http://www.access.edu.au                    *   *           Copyright 2003-2007 by ACceSS MNRF
7   *       Primary Business: Queensland, Australia            *   *       Copyright 2007 by University of Queensland
8   *  Licensed under the Open Software License version 3.0    *   *
9   *     http://www.opensource.org/licenses/osl-3.0.php       *   *                http://esscc.uq.edu.au
10   *                                                          *   *        Primary Business: Queensland, Australia
11   ************************************************************   *  Licensed under the Open Software License version 3.0
12  */   *     http://www.opensource.org/licenses/osl-3.0.php
13     *
14     *******************************************************/
15    
16  /** \file Data.h */  /** \file Data.h */
17    
# Line 26  Line 28 
28    
29  extern "C" {  extern "C" {
30  #include "DataC.h"  #include "DataC.h"
31  #include "paso/Paso.h"  /* #include "paso/Paso.h" doesn't belong in this file...causes trouble for BruceFactory.cpp */
32  }  }
33    
34  #ifndef PASO_MPI  #include "esysmpi.h"
 #define MPI_Comm long  
 #endif  
   
35  #include <string>  #include <string>
36  #include <algorithm>  #include <algorithm>
37    #include <sstream>
38    
39    
40  #include <boost/shared_ptr.hpp>  #include <boost/shared_ptr.hpp>
41  #include <boost/python/object.hpp>  #include <boost/python/object.hpp>
# Line 51  class DataExpanded; Line 52  class DataExpanded;
52    
53  /**  /**
54     \brief     \brief
55     Data creates the appropriate Data object for the given construction     Data creates the appropriate Data object for the given construction
56     arguments.     arguments.
57    
58     Description:     Description:
59     Data is essentially a factory class which creates the appropriate Data     Data is essentially a factory class which creates the appropriate Data
# Line 214  class Data { Line 215  class Data {
215       Constructor which creates a DataConstant of "shape" with constant value.       Constructor which creates a DataConstant of "shape" with constant value.
216    */    */
217    ESCRIPT_DLL_API    ESCRIPT_DLL_API
218    Data(double value,    Data(double value,
219         const boost::python::tuple& shape=boost::python::make_tuple(),         const boost::python::tuple& shape=boost::python::make_tuple(),
220         const FunctionSpace& what=FunctionSpace(),         const FunctionSpace& what=FunctionSpace(),
221         bool expanded=false);         bool expanded=false);
222    /**    /**
# Line 239  class Data { Line 240  class Data {
240    
241    /**    /**
242       \brief       \brief
243       switches on update protection       switches on update protection
244    
245    */    */
246    ESCRIPT_DLL_API    ESCRIPT_DLL_API
# Line 257  class Data { Line 258  class Data {
258    
259    /**    /**
260       \brief       \brief
261       Return the values of a data point on this process       Return the values of a data point on this process
262    */    */
263    ESCRIPT_DLL_API    ESCRIPT_DLL_API
264    const boost::python::numeric::array    const boost::python::numeric::array
# Line 299  class Data { Line 300  class Data {
300       \brief       \brief
301       Return the tag number associated with the given data-point.       Return the tag number associated with the given data-point.
302    
      The data-point number here corresponds to the data-point number in the  
      numarray returned by convertToNumArray.  
303    */    */
304    ESCRIPT_DLL_API    ESCRIPT_DLL_API
305    int    int
# Line 324  class Data { Line 323  class Data {
323    
324    /**    /**
325       \brief       \brief
326       Write the data as a string.       Write the data as a string. For large amounts of data, a summary is printed.
327    */    */
328    ESCRIPT_DLL_API    ESCRIPT_DLL_API
   inline  
329    std::string    std::string
330    toString() const    toString() const;
331    {  
     return m_data->toString();  
   }  
332    
333    /**    /**
334       \brief       \brief
# Line 487  class Data { Line 483  class Data {
483    }    }
484    /**    /**
485       \brief       \brief
486       dumps the object into a netCDF file       dumps the object into a netCDF file
487    */    */
488    ESCRIPT_DLL_API    ESCRIPT_DLL_API
   inline  
489    void    void
490    dump(const std::string fileName) const    dump(const std::string fileName) const;
   {  
     return m_data->dump(fileName);  
   }  
   
491    /**    /**
492       \brief       \brief
493       Return the sample data for the given sample no. This is not the       Return the sample data for the given sample no. This is not the
# Line 539  class Data { Line 530  class Data {
530    getDataPoint(int sampleNo,    getDataPoint(int sampleNo,
531                 int dataPointNo)                 int dataPointNo)
532    {    {
533          return m_data->getDataPoint(sampleNo,dataPointNo);                  return m_data->getDataPoint(sampleNo,dataPointNo);
534    }    }
535    
536    /**    /**
# Line 594  class Data { Line 585  class Data {
585    /**    /**
586       \brief       \brief
587       Assign the given value to the tag. Implicitly converts this       Assign the given value to the tag. Implicitly converts this
588       object to type DataTagged. Throws an exception if this object       object to type DataTagged if it is constant.
589       cannot be converted to a DataTagged object.  
590       \param tagKey - Input - Integer key.       \param tagKey - Input - Integer key.
591       \param value - Input - Value to associate with given key.       \param value - Input - Value to associate with given key.
592      ==>*      ==>*
# Line 608  class Data { Line 599  class Data {
599    /**    /**
600       \brief       \brief
601       Assign the given value to the tag. Implicitly converts this       Assign the given value to the tag. Implicitly converts this
602       object to type DataTagged. Throws an exception if this object       object to type DataTagged if it is constant.
603       cannot be converted to a DataTagged object.  
604       \param tagKey - Input - Integer key.       \param tagKey - Input - Integer key.
605       \param value - Input - Value to associate with given key.       \param value - Input - Value to associate with given key.
606      ==>*      ==>*
# Line 634  class Data { Line 625  class Data {
625    
626    /**    /**
627       \brief       \brief
628         set all values to zero
629         *
630      */
631      ESCRIPT_DLL_API
632      void
633      setToZero();
634    
635      /**
636         \brief
637       Interpolates this onto the given functionspace and returns       Interpolates this onto the given functionspace and returns
638       the result as a Data object.       the result as a Data object.
639       *       *
# Line 641  class Data { Line 641  class Data {
641    ESCRIPT_DLL_API    ESCRIPT_DLL_API
642    Data    Data
643    interpolate(const FunctionSpace& functionspace) const;    interpolate(const FunctionSpace& functionspace) const;
   
644    /**    /**
645       \brief       \brief
646       Calculates the gradient of the data at the data points of functionspace.       Calculates the gradient of the data at the data points of functionspace.
# Line 738  class Data { Line 737  class Data {
737    
738    /**    /**
739       \brief       \brief
      Return the minimum absolute value of this Data object.  
      *  
   */  
   ESCRIPT_DLL_API  
   double  
   Linf() const;  
   
   /**  
      \brief  
740       Return the maximum value of this Data object.       Return the maximum value of this Data object.
741       *       *
742    */    */
# Line 861  class Data { Line 851  class Data {
851    /**    /**
852       \brief       \brief
853       Return the eigenvalues and corresponding eigenvcetors of the symmetric part at each data point of this Data object.       Return the eigenvalues and corresponding eigenvcetors of the symmetric part at each data point of this Data object.
854       the eigenvalues are ordered in increasing size where eigenvalues with relative difference less than       the eigenvalues are ordered in increasing size where eigenvalues with relative difference less than
855       tol are treated as equal. The eigenvectors are orthogonal, normalized and the sclaed such that the       tol are treated as equal. The eigenvectors are orthogonal, normalized and the sclaed such that the
856       first non-zero entry is positive.       first non-zero entry is positive.
857       Currently this function is restricted to rank 2, square shape, and dimension 3       Currently this function is restricted to rank 2, square shape, and dimension 3
858       *       *
# Line 1066  class Data { Line 1056  class Data {
1056    /**    /**
1057       \brief       \brief
1058       Return the given power of each data point of this boost python object.       Return the given power of each data point of this boost python object.
1059        
1060       \param right Input - the power to raise the object to.       \param right Input - the power to raise the object to.
1061       *       *
1062     */     */
# Line 1077  class Data { Line 1067  class Data {
1067    /**    /**
1068       \brief       \brief
1069       Return the given power of each data point of this boost python object.       Return the given power of each data point of this boost python object.
1070        
1071       \param left Input - the bases       \param left Input - the bases
1072       *       *
1073     */     */
# Line 1113  class Data { Line 1103  class Data {
1103    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1104    Data& operator+=(const boost::python::object& right);    Data& operator+=(const boost::python::object& right);
1105    
1106      ESCRIPT_DLL_API
1107      Data& operator=(const Data& other);
1108    
1109    /**    /**
1110       \brief       \brief
1111       Overloaded operator -=       Overloaded operator -=
# Line 1205  class Data { Line 1198  class Data {
1198    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1199    inline    inline
1200    void    void
1201    unaryOp(UnaryFunction operation);    unaryOp2(UnaryFunction operation);
1202    
1203    /**    /**
1204       \brief       \brief
# Line 1256  class Data { Line 1249  class Data {
1249    
1250    /**    /**
1251       \brief       \brief
1252       print the data values to stdout. Used for debugging       print the data values to stdout. Used for debugging
1253    */    */
1254    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1255    void    void
1256      print(void);          print(void);
1257    
1258    /**    /**
1259       \brief       \brief
1260       return the MPI rank number of the local data       return the MPI rank number of the local data
1261           MPI_COMM_WORLD is assumed and the result of MPI_Comm_size()                   MPI_COMM_WORLD is assumed and the result of MPI_Comm_size()
1262           is returned                   is returned
1263    */    */
1264    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1265      int          int
1266      get_MPIRank(void) const;          get_MPIRank(void) const;
1267    
1268    /**    /**
1269       \brief       \brief
1270       return the MPI rank number of the local data       return the MPI rank number of the local data
1271           MPI_COMM_WORLD is assumed and the result of MPI_Comm_rank()                   MPI_COMM_WORLD is assumed and the result of MPI_Comm_rank()
1272           is returned                   is returned
1273    */    */
1274    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1275      int          int
1276      get_MPISize(void) const;          get_MPISize(void) const;
1277    
1278    /**    /**
1279       \brief       \brief
1280       return the MPI rank number of the local data       return the MPI rank number of the local data
1281           MPI_COMM_WORLD is assumed and returned.                   MPI_COMM_WORLD is assumed and returned.
1282    */    */
1283    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1284      MPI_Comm          MPI_Comm
1285      get_MPIComm(void) const;          get_MPIComm(void) const;
1286    
1287    /**    /**
1288       \brief       \brief
1289       return the object produced by the factory, which is a DataConstant or DataExpanded       return the object produced by the factory, which is a DataConstant or DataExpanded
1290    */    */
1291    ESCRIPT_DLL_API    ESCRIPT_DLL_API
1292      DataAbstract*          DataAbstract*
1293      borrowData(void) const;          borrowData(void) const;
1294    
1295   protected:   protected:
1296    
# Line 1413  Data::initialise(const IValueType& value Line 1406  Data::initialise(const IValueType& value
1406  }  }
1407    
1408  /**  /**
1409       Modify a filename for MPI parallel output to multiple files
1410    */
1411    char *Escript_MPI_appendRankToFileName(const char *, int, int);
1412    
1413    /**
1414     Binary Data object operators.     Binary Data object operators.
1415  */  */
1416  inline double rpow(double x,double y)  inline double rpow(double x,double y)
1417  {  {
1418      return pow(y,x);      return pow(y,x);
1419  }  }
# Line 1512  ESCRIPT_DLL_API Data operator*(const boo Line 1510  ESCRIPT_DLL_API Data operator*(const boo
1510  */  */
1511  ESCRIPT_DLL_API Data operator/(const boost::python::object& left, const Data& right);  ESCRIPT_DLL_API Data operator/(const boost::python::object& left, const Data& right);
1512    
1513    
1514    
1515  /**  /**
1516    \brief    \brief
1517    Output operator    Output operator
# Line 1533  C_GeneralTensorProduct(Data& arg0, Line 1533  C_GeneralTensorProduct(Data& arg0,
1533                       int axis_offset=0,                       int axis_offset=0,
1534                       int transpose=0);                       int transpose=0);
1535    
1536    
1537    
1538  /**  /**
1539    \brief    \brief
1540    Return true if operands are equivalent, else return false.    Return true if operands are equivalent, else return false.
1541    NB: this operator does very little at this point, and isn't to    NB: this operator does very little at this point, and isn't to
1542    be relied on. Requires further implementation.    be relied on. Requires further implementation.
1543  */  */
1544  //ESCRIPT_DLL_API bool operator==(const Data& left, const Data& right);  // ESCRIPT_DLL_API bool operator==(const Data& left, const Data& right);
1545    
1546  /**  /**
1547    \brief    \brief
# Line 1563  Data::binaryOp(const Data& right, Line 1565  Data::binaryOp(const Data& right,
1565     if (getFunctionSpace()!=right.getFunctionSpace()) {     if (getFunctionSpace()!=right.getFunctionSpace()) {
1566       if (right.probeInterpolation(getFunctionSpace())) {       if (right.probeInterpolation(getFunctionSpace())) {
1567         //         //
1568         // an interpolation is required so create a new Data         // an interpolation is required so create a new Data
1569         tempRight=Data(right,this->getFunctionSpace());         tempRight=Data(right,this->getFunctionSpace());
1570       } else if (probeInterpolation(right.getFunctionSpace())) {       } else if (probeInterpolation(right.getFunctionSpace())) {
1571         //         //
# Line 1611  Data::binaryOp(const Data& right, Line 1613  Data::binaryOp(const Data& right,
1613    
1614  /**  /**
1615    \brief    \brief
   Perform the given unary operation on other and return the result.  
   Given operation is performed on each element of each data point, thus  
   argument object is a rank n Data object, and returned object is a rank n  
   Data object.  
   Calls Data::unaryOp.  
 */  
 template <class UnaryFunction>  
 inline  
 Data  
 unaryOp(const Data& other,  
         UnaryFunction operation)  
 {  
   Data result;  
   result.copy(other);  
   result.unaryOp(operation);  
   return result;  
 }  
   
 /**  
   \brief  
   Perform the given unary operation on this.  
   Given operation is performed on each element of each data point.  
   Calls escript::unaryOp.  
 */  
 template <class UnaryFunction>  
 inline  
 void  
 Data::unaryOp(UnaryFunction operation)  
 {  
   if (isExpanded()) {  
     DataExpanded* leftC=dynamic_cast<DataExpanded*>(m_data.get());  
     EsysAssert((leftC!=0), "Programming error - casting to DataExpanded.");  
     escript::unaryOp(*leftC,operation);  
   } else if (isTagged()) {  
     DataTagged* leftC=dynamic_cast<DataTagged*>(m_data.get());  
     EsysAssert((leftC!=0), "Programming error - casting to DataTagged.");  
     escript::unaryOp(*leftC,operation);  
   } else if (isConstant()) {  
     DataConstant* leftC=dynamic_cast<DataConstant*>(m_data.get());  
     EsysAssert((leftC!=0), "Programming error - casting to DataConstant.");  
     escript::unaryOp(*leftC,operation);  
   }  
 }  
   
 /**  
   \brief  
1616    Perform the given Data object reduction algorithm on this and return the result.    Perform the given Data object reduction algorithm on this and return the result.
1617    Given operation combines each element of each data point, thus argument    Given operation combines each element of each data point, thus argument
1618    object (*this) is a rank n Data object, and returned object is a scalar.    object (*this) is a rank n Data object, and returned object is a scalar.
# Line 1687  Data::algorithm(BinaryFunction operation Line 1643  Data::algorithm(BinaryFunction operation
1643    \brief    \brief
1644    Perform the given data point reduction algorithm on data and return the result.    Perform the given data point reduction algorithm on data and return the result.
1645    Given operation combines each element within each data point into a scalar,    Given operation combines each element within each data point into a scalar,
1646    thus argument object is a rank n Data object, and returned object is a    thus argument object is a rank n Data object, and returned object is a
1647    rank 0 Data object.    rank 0 Data object.
1648    Calls escript::dp_algorithm.    Calls escript::dp_algorithm.
1649  */  */
# Line 1736  Data::dp_algorithm(BinaryFunction operat Line 1692  Data::dp_algorithm(BinaryFunction operat
1692    return falseRetVal;    return falseRetVal;
1693  }  }
1694    
1695    /**
1696      \brief
1697      Compute a tensor operation with two Data objects
1698      \param arg0 - Input - Data object
1699      \param arg1 - Input - Data object
1700      \param operation - Input - Binary op functor
1701    */
1702    template <typename BinaryFunction>
1703    inline
1704    Data
1705    C_TensorBinaryOperation(Data const &arg_0,
1706                            Data const &arg_1,
1707                            BinaryFunction operation)
1708    {
1709      // Interpolate if necessary and find an appropriate function space
1710      Data arg_0_Z, arg_1_Z;
1711      if (arg_0.getFunctionSpace()!=arg_1.getFunctionSpace()) {
1712        if (arg_0.probeInterpolation(arg_1.getFunctionSpace())) {
1713          arg_0_Z = arg_0.interpolate(arg_1.getFunctionSpace());
1714          arg_1_Z = Data(arg_1);
1715        }
1716        else if (arg_1.probeInterpolation(arg_0.getFunctionSpace())) {
1717          arg_1_Z=arg_1.interpolate(arg_0.getFunctionSpace());
1718          arg_0_Z =Data(arg_0);
1719        }
1720        else {
1721          throw DataException("Error - C_TensorBinaryOperation: arguments have incompatible function spaces.");
1722        }
1723      } else {
1724          arg_0_Z = Data(arg_0);
1725          arg_1_Z = Data(arg_1);
1726      }
1727      // Get rank and shape of inputs
1728      int rank0 = arg_0_Z.getDataPointRank();
1729      int rank1 = arg_1_Z.getDataPointRank();
1730      DataArrayView::ShapeType shape0 = arg_0_Z.getDataPointShape();
1731      DataArrayView::ShapeType shape1 = arg_1_Z.getDataPointShape();
1732      int size0 = arg_0_Z.getDataPointSize();
1733      int size1 = arg_1_Z.getDataPointSize();
1734    
1735      // Declare output Data object
1736      Data res;
1737    
1738      if (shape0 == shape1) {
1739    
1740        if (arg_0_Z.isConstant()   && arg_1_Z.isConstant()) {
1741          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());      // DataConstant output
1742          double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[0]);
1743          double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[0]);
1744          double *ptr_2 = &((res.getPointDataView().getData())[0]);
1745          tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1746        }
1747        else if (arg_0_Z.isConstant()   && arg_1_Z.isTagged()) {
1748    
1749          // Prepare the DataConstant input
1750          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
1751    
1752          // Borrow DataTagged input from Data object
1753          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
1754    
1755          // Prepare a DataTagged output 2
1756          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());      // DataTagged output
1757          res.tag();
1758          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
1759    
1760          // Prepare offset into DataConstant
1761          int offset_0 = tmp_0->getPointOffset(0,0);
1762          double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
1763          // Get the views
1764          DataArrayView view_1 = tmp_1->getDefaultValue();
1765          DataArrayView view_2 = tmp_2->getDefaultValue();
1766          // Get the pointers to the actual data
1767          double *ptr_1 = &((view_1.getData())[0]);
1768          double *ptr_2 = &((view_2.getData())[0]);
1769          // Compute a result for the default
1770          tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1771          // Compute a result for each tag
1772          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
1773          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
1774          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
1775            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue());
1776            DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
1777            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
1778            double *ptr_1 = &view_1.getData(0);
1779            double *ptr_2 = &view_2.getData(0);
1780            tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1781          }
1782    
1783        }
1784        else if (arg_0_Z.isConstant()   && arg_1_Z.isExpanded()) {
1785    
1786          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
1787          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
1788          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
1789          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
1790    
1791          int sampleNo_1,dataPointNo_1;
1792          int numSamples_1 = arg_1_Z.getNumSamples();
1793          int numDataPointsPerSample_1 = arg_1_Z.getNumDataPointsPerSample();
1794          int offset_0 = tmp_0->getPointOffset(0,0);
1795          #pragma omp parallel for private(sampleNo_1,dataPointNo_1) schedule(static)
1796          for (sampleNo_1 = 0; sampleNo_1 < numSamples_1; sampleNo_1++) {
1797            for (dataPointNo_1 = 0; dataPointNo_1 < numDataPointsPerSample_1; dataPointNo_1++) {
1798              int offset_1 = tmp_1->getPointOffset(sampleNo_1,dataPointNo_1);
1799              int offset_2 = tmp_2->getPointOffset(sampleNo_1,dataPointNo_1);
1800              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
1801              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
1802              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
1803              tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1804            }
1805          }
1806    
1807        }
1808        else if (arg_0_Z.isTagged()     && arg_1_Z.isConstant()) {
1809    
1810          // Borrow DataTagged input from Data object
1811          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
1812    
1813          // Prepare the DataConstant input
1814          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
1815    
1816          // Prepare a DataTagged output 2
1817          res = Data(0.0, shape0, arg_0_Z.getFunctionSpace());      // DataTagged output
1818          res.tag();
1819          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
1820    
1821          // Prepare offset into DataConstant
1822          int offset_1 = tmp_1->getPointOffset(0,0);
1823          double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
1824          // Get the views
1825          DataArrayView view_0 = tmp_0->getDefaultValue();
1826          DataArrayView view_2 = tmp_2->getDefaultValue();
1827          // Get the pointers to the actual data
1828          double *ptr_0 = &((view_0.getData())[0]);
1829          double *ptr_2 = &((view_2.getData())[0]);
1830          // Compute a result for the default
1831          tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1832          // Compute a result for each tag
1833          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
1834          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
1835          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
1836            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue());
1837            DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
1838            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
1839            double *ptr_0 = &view_0.getData(0);
1840            double *ptr_2 = &view_2.getData(0);
1841            tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1842          }
1843    
1844        }
1845        else if (arg_0_Z.isTagged()     && arg_1_Z.isTagged()) {
1846    
1847          // Borrow DataTagged input from Data object
1848          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
1849    
1850          // Borrow DataTagged input from Data object
1851          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
1852    
1853          // Prepare a DataTagged output 2
1854          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());
1855          res.tag();        // DataTagged output
1856          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
1857    
1858          // Get the views
1859          DataArrayView view_0 = tmp_0->getDefaultValue();
1860          DataArrayView view_1 = tmp_1->getDefaultValue();
1861          DataArrayView view_2 = tmp_2->getDefaultValue();
1862          // Get the pointers to the actual data
1863          double *ptr_0 = &((view_0.getData())[0]);
1864          double *ptr_1 = &((view_1.getData())[0]);
1865          double *ptr_2 = &((view_2.getData())[0]);
1866          // Compute a result for the default
1867          tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1868          // Merge the tags
1869          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
1870          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
1871          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
1872          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
1873            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue()); // use tmp_2 to get correct shape
1874          }
1875          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
1876            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue());
1877          }
1878          // Compute a result for each tag
1879          const DataTagged::DataMapType& lookup_2=tmp_2->getTagLookup();
1880          for (i=lookup_2.begin();i!=lookup_2.end();i++) {
1881            DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
1882            DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
1883            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
1884            double *ptr_0 = &view_0.getData(0);
1885            double *ptr_1 = &view_1.getData(0);
1886            double *ptr_2 = &view_2.getData(0);
1887            tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1888          }
1889    
1890        }
1891        else if (arg_0_Z.isTagged()     && arg_1_Z.isExpanded()) {
1892    
1893          // After finding a common function space above the two inputs have the same numSamples and num DPPS
1894          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
1895          DataTagged*   tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
1896          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
1897          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
1898    
1899          int sampleNo_0,dataPointNo_0;
1900          int numSamples_0 = arg_0_Z.getNumSamples();
1901          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
1902          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
1903          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
1904            int offset_0 = tmp_0->getPointOffset(sampleNo_0,0); // They're all the same, so just use #0
1905            double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
1906            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
1907              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
1908              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
1909              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
1910              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
1911              tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1912            }
1913          }
1914    
1915        }
1916        else if (arg_0_Z.isExpanded()   && arg_1_Z.isConstant()) {
1917    
1918          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
1919          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
1920          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
1921          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
1922    
1923          int sampleNo_0,dataPointNo_0;
1924          int numSamples_0 = arg_0_Z.getNumSamples();
1925          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
1926          int offset_1 = tmp_1->getPointOffset(0,0);
1927          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
1928          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
1929            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
1930              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
1931              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
1932              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
1933              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
1934              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
1935              tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1936            }
1937          }
1938    
1939        }
1940        else if (arg_0_Z.isExpanded()   && arg_1_Z.isTagged()) {
1941    
1942          // After finding a common function space above the two inputs have the same numSamples and num DPPS
1943          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
1944          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
1945          DataTagged*   tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
1946          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
1947    
1948          int sampleNo_0,dataPointNo_0;
1949          int numSamples_0 = arg_0_Z.getNumSamples();
1950          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
1951          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
1952          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
1953            int offset_1 = tmp_1->getPointOffset(sampleNo_0,0);
1954            double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
1955            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
1956              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
1957              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
1958              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
1959              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
1960              tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1961            }
1962          }
1963    
1964        }
1965        else if (arg_0_Z.isExpanded()   && arg_1_Z.isExpanded()) {
1966    
1967          // After finding a common function space above the two inputs have the same numSamples and num DPPS
1968          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
1969          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
1970          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
1971          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
1972    
1973          int sampleNo_0,dataPointNo_0;
1974          int numSamples_0 = arg_0_Z.getNumSamples();
1975          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
1976          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
1977          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
1978            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
1979              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
1980              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
1981              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
1982              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
1983              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
1984              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
1985              tensor_binary_operation(size0, ptr_0, ptr_1, ptr_2, operation);
1986            }
1987          }
1988    
1989        }
1990        else {
1991          throw DataException("Error - C_TensorBinaryOperation: unknown combination of inputs");
1992        }
1993    
1994      } else if (0 == rank0) {
1995    
1996        if (arg_0_Z.isConstant()   && arg_1_Z.isConstant()) {
1997          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace());      // DataConstant output
1998          double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[0]);
1999          double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[0]);
2000          double *ptr_2 = &((res.getPointDataView().getData())[0]);
2001          tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2002        }
2003        else if (arg_0_Z.isConstant()   && arg_1_Z.isTagged()) {
2004    
2005          // Prepare the DataConstant input
2006          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
2007    
2008          // Borrow DataTagged input from Data object
2009          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2010    
2011          // Prepare a DataTagged output 2
2012          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace());      // DataTagged output
2013          res.tag();
2014          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2015    
2016          // Prepare offset into DataConstant
2017          int offset_0 = tmp_0->getPointOffset(0,0);
2018          double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2019          // Get the views
2020          DataArrayView view_1 = tmp_1->getDefaultValue();
2021          DataArrayView view_2 = tmp_2->getDefaultValue();
2022          // Get the pointers to the actual data
2023          double *ptr_1 = &((view_1.getData())[0]);
2024          double *ptr_2 = &((view_2.getData())[0]);
2025          // Compute a result for the default
2026          tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2027          // Compute a result for each tag
2028          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
2029          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2030          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
2031            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue());
2032            DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
2033            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2034            double *ptr_1 = &view_1.getData(0);
2035            double *ptr_2 = &view_2.getData(0);
2036            tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2037          }
2038    
2039        }
2040        else if (arg_0_Z.isConstant()   && arg_1_Z.isExpanded()) {
2041    
2042          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2043          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
2044          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2045          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2046    
2047          int sampleNo_1,dataPointNo_1;
2048          int numSamples_1 = arg_1_Z.getNumSamples();
2049          int numDataPointsPerSample_1 = arg_1_Z.getNumDataPointsPerSample();
2050          int offset_0 = tmp_0->getPointOffset(0,0);
2051          #pragma omp parallel for private(sampleNo_1,dataPointNo_1) schedule(static)
2052          for (sampleNo_1 = 0; sampleNo_1 < numSamples_1; sampleNo_1++) {
2053            for (dataPointNo_1 = 0; dataPointNo_1 < numDataPointsPerSample_1; dataPointNo_1++) {
2054              int offset_1 = tmp_1->getPointOffset(sampleNo_1,dataPointNo_1);
2055              int offset_2 = tmp_2->getPointOffset(sampleNo_1,dataPointNo_1);
2056              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2057              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2058              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2059              tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2060    
2061            }
2062          }
2063    
2064        }
2065        else if (arg_0_Z.isTagged()     && arg_1_Z.isConstant()) {
2066    
2067          // Borrow DataTagged input from Data object
2068          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2069    
2070          // Prepare the DataConstant input
2071          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
2072    
2073          // Prepare a DataTagged output 2
2074          res = Data(0.0, shape1, arg_0_Z.getFunctionSpace());      // DataTagged output
2075          res.tag();
2076          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2077    
2078          // Prepare offset into DataConstant
2079          int offset_1 = tmp_1->getPointOffset(0,0);
2080          double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2081          // Get the views
2082          DataArrayView view_0 = tmp_0->getDefaultValue();
2083          DataArrayView view_2 = tmp_2->getDefaultValue();
2084          // Get the pointers to the actual data
2085          double *ptr_0 = &((view_0.getData())[0]);
2086          double *ptr_2 = &((view_2.getData())[0]);
2087          // Compute a result for the default
2088          tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2089          // Compute a result for each tag
2090          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
2091          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2092          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
2093            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue());
2094            DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
2095            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2096            double *ptr_0 = &view_0.getData(0);
2097            double *ptr_2 = &view_2.getData(0);
2098            tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2099          }
2100    
2101        }
2102        else if (arg_0_Z.isTagged()     && arg_1_Z.isTagged()) {
2103    
2104          // Borrow DataTagged input from Data object
2105          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2106    
2107          // Borrow DataTagged input from Data object
2108          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2109    
2110          // Prepare a DataTagged output 2
2111          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace());
2112          res.tag();        // DataTagged output
2113          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2114    
2115          // Get the views
2116          DataArrayView view_0 = tmp_0->getDefaultValue();
2117          DataArrayView view_1 = tmp_1->getDefaultValue();
2118          DataArrayView view_2 = tmp_2->getDefaultValue();
2119          // Get the pointers to the actual data
2120          double *ptr_0 = &((view_0.getData())[0]);
2121          double *ptr_1 = &((view_1.getData())[0]);
2122          double *ptr_2 = &((view_2.getData())[0]);
2123          // Compute a result for the default
2124          tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2125          // Merge the tags
2126          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2127          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
2128          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
2129          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
2130            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue()); // use tmp_2 to get correct shape
2131          }
2132          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
2133            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue());
2134          }
2135          // Compute a result for each tag
2136          const DataTagged::DataMapType& lookup_2=tmp_2->getTagLookup();
2137          for (i=lookup_2.begin();i!=lookup_2.end();i++) {
2138            DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
2139            DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
2140            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2141            double *ptr_0 = &view_0.getData(0);
2142            double *ptr_1 = &view_1.getData(0);
2143            double *ptr_2 = &view_2.getData(0);
2144            tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2145          }
2146    
2147        }
2148        else if (arg_0_Z.isTagged()     && arg_1_Z.isExpanded()) {
2149    
2150          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2151          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2152          DataTagged*   tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2153          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2154          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2155    
2156          int sampleNo_0,dataPointNo_0;
2157          int numSamples_0 = arg_0_Z.getNumSamples();
2158          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2159          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2160          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2161            int offset_0 = tmp_0->getPointOffset(sampleNo_0,0); // They're all the same, so just use #0
2162            double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2163            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2164              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
2165              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2166              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2167              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2168              tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2169            }
2170          }
2171    
2172        }
2173        else if (arg_0_Z.isExpanded()   && arg_1_Z.isConstant()) {
2174    
2175          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2176          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2177          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
2178          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2179    
2180          int sampleNo_0,dataPointNo_0;
2181          int numSamples_0 = arg_0_Z.getNumSamples();
2182          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2183          int offset_1 = tmp_1->getPointOffset(0,0);
2184          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2185          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2186            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2187              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2188              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2189              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2190              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2191              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2192              tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2193            }
2194          }
2195    
2196    
2197        }
2198        else if (arg_0_Z.isExpanded()   && arg_1_Z.isTagged()) {
2199    
2200          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2201          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2202          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2203          DataTagged*   tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2204          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2205    
2206          int sampleNo_0,dataPointNo_0;
2207          int numSamples_0 = arg_0_Z.getNumSamples();
2208          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2209          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2210          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2211            int offset_1 = tmp_1->getPointOffset(sampleNo_0,0);
2212            double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2213            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2214              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2215              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2216              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2217              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2218              tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2219            }
2220          }
2221    
2222        }
2223        else if (arg_0_Z.isExpanded()   && arg_1_Z.isExpanded()) {
2224    
2225          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2226          res = Data(0.0, shape1, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2227          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2228          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2229          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2230    
2231          int sampleNo_0,dataPointNo_0;
2232          int numSamples_0 = arg_0_Z.getNumSamples();
2233          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2234          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2235          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2236            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2237              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2238              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
2239              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2240              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2241              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2242              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2243              tensor_binary_operation(size1, ptr_0[0], ptr_1, ptr_2, operation);
2244            }
2245          }
2246    
2247        }
2248        else {
2249          throw DataException("Error - C_TensorBinaryOperation: unknown combination of inputs");
2250        }
2251    
2252      } else if (0 == rank1) {
2253    
2254        if (arg_0_Z.isConstant()   && arg_1_Z.isConstant()) {
2255          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());      // DataConstant output
2256          double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[0]);
2257          double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[0]);
2258          double *ptr_2 = &((res.getPointDataView().getData())[0]);
2259          tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2260        }
2261        else if (arg_0_Z.isConstant()   && arg_1_Z.isTagged()) {
2262    
2263          // Prepare the DataConstant input
2264          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
2265    
2266          // Borrow DataTagged input from Data object
2267          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2268    
2269          // Prepare a DataTagged output 2
2270          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());      // DataTagged output
2271          res.tag();
2272          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2273    
2274          // Prepare offset into DataConstant
2275          int offset_0 = tmp_0->getPointOffset(0,0);
2276          double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2277          // Get the views
2278          DataArrayView view_1 = tmp_1->getDefaultValue();
2279          DataArrayView view_2 = tmp_2->getDefaultValue();
2280          // Get the pointers to the actual data
2281          double *ptr_1 = &((view_1.getData())[0]);
2282          double *ptr_2 = &((view_2.getData())[0]);
2283          // Compute a result for the default
2284          tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2285          // Compute a result for each tag
2286          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
2287          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2288          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
2289            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue());
2290            DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
2291            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2292            double *ptr_1 = &view_1.getData(0);
2293            double *ptr_2 = &view_2.getData(0);
2294            tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2295          }
2296    
2297        }
2298        else if (arg_0_Z.isConstant()   && arg_1_Z.isExpanded()) {
2299    
2300          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2301          DataConstant* tmp_0=dynamic_cast<DataConstant*>(arg_0_Z.borrowData());
2302          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2303          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2304    
2305          int sampleNo_1,dataPointNo_1;
2306          int numSamples_1 = arg_1_Z.getNumSamples();
2307          int numDataPointsPerSample_1 = arg_1_Z.getNumDataPointsPerSample();
2308          int offset_0 = tmp_0->getPointOffset(0,0);
2309          #pragma omp parallel for private(sampleNo_1,dataPointNo_1) schedule(static)
2310          for (sampleNo_1 = 0; sampleNo_1 < numSamples_1; sampleNo_1++) {
2311            for (dataPointNo_1 = 0; dataPointNo_1 < numDataPointsPerSample_1; dataPointNo_1++) {
2312              int offset_1 = tmp_1->getPointOffset(sampleNo_1,dataPointNo_1);
2313              int offset_2 = tmp_2->getPointOffset(sampleNo_1,dataPointNo_1);
2314              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2315              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2316              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2317              tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2318            }
2319          }
2320    
2321        }
2322        else if (arg_0_Z.isTagged()     && arg_1_Z.isConstant()) {
2323    
2324          // Borrow DataTagged input from Data object
2325          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2326    
2327          // Prepare the DataConstant input
2328          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
2329    
2330          // Prepare a DataTagged output 2
2331          res = Data(0.0, shape0, arg_0_Z.getFunctionSpace());      // DataTagged output
2332          res.tag();
2333          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2334    
2335          // Prepare offset into DataConstant
2336          int offset_1 = tmp_1->getPointOffset(0,0);
2337          double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2338          // Get the views
2339          DataArrayView view_0 = tmp_0->getDefaultValue();
2340          DataArrayView view_2 = tmp_2->getDefaultValue();
2341          // Get the pointers to the actual data
2342          double *ptr_0 = &((view_0.getData())[0]);
2343          double *ptr_2 = &((view_2.getData())[0]);
2344          // Compute a result for the default
2345          tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2346          // Compute a result for each tag
2347          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
2348          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2349          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
2350            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue());
2351            DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
2352            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2353            double *ptr_0 = &view_0.getData(0);
2354            double *ptr_2 = &view_2.getData(0);
2355            tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2356          }
2357    
2358        }
2359        else if (arg_0_Z.isTagged()     && arg_1_Z.isTagged()) {
2360    
2361          // Borrow DataTagged input from Data object
2362          DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2363    
2364          // Borrow DataTagged input from Data object
2365          DataTagged* tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2366    
2367          // Prepare a DataTagged output 2
2368          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace());
2369          res.tag();        // DataTagged output
2370          DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2371    
2372          // Get the views
2373          DataArrayView view_0 = tmp_0->getDefaultValue();
2374          DataArrayView view_1 = tmp_1->getDefaultValue();
2375          DataArrayView view_2 = tmp_2->getDefaultValue();
2376          // Get the pointers to the actual data
2377          double *ptr_0 = &((view_0.getData())[0]);
2378          double *ptr_1 = &((view_1.getData())[0]);
2379          double *ptr_2 = &((view_2.getData())[0]);
2380          // Compute a result for the default
2381          tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2382          // Merge the tags
2383          DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2384          const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
2385          const DataTagged::DataMapType& lookup_1=tmp_1->getTagLookup();
2386          for (i=lookup_0.begin();i!=lookup_0.end();i++) {
2387            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue()); // use tmp_2 to get correct shape
2388          }
2389          for (i=lookup_1.begin();i!=lookup_1.end();i++) {
2390            tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue());
2391          }
2392          // Compute a result for each tag
2393          const DataTagged::DataMapType& lookup_2=tmp_2->getTagLookup();
2394          for (i=lookup_2.begin();i!=lookup_2.end();i++) {
2395            DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
2396            DataArrayView view_1 = tmp_1->getDataPointByTag(i->first);
2397            DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2398            double *ptr_0 = &view_0.getData(0);
2399            double *ptr_1 = &view_1.getData(0);
2400            double *ptr_2 = &view_2.getData(0);
2401            tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2402          }
2403    
2404        }
2405        else if (arg_0_Z.isTagged()     && arg_1_Z.isExpanded()) {
2406    
2407          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2408          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2409          DataTagged*   tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2410          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2411          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2412    
2413          int sampleNo_0,dataPointNo_0;
2414          int numSamples_0 = arg_0_Z.getNumSamples();
2415          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2416          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2417          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2418            int offset_0 = tmp_0->getPointOffset(sampleNo_0,0); // They're all the same, so just use #0
2419            double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2420            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2421              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
2422              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2423              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2424              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2425              tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2426            }
2427          }
2428    
2429        }
2430        else if (arg_0_Z.isExpanded()   && arg_1_Z.isConstant()) {
2431    
2432          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2433          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2434          DataConstant* tmp_1=dynamic_cast<DataConstant*>(arg_1_Z.borrowData());
2435          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2436    
2437          int sampleNo_0,dataPointNo_0;
2438          int numSamples_0 = arg_0_Z.getNumSamples();
2439          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2440          int offset_1 = tmp_1->getPointOffset(0,0);
2441          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2442          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2443            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2444              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2445              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2446              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2447              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2448              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2449              tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2450            }
2451          }
2452    
2453    
2454        }
2455        else if (arg_0_Z.isExpanded()   && arg_1_Z.isTagged()) {
2456    
2457          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2458          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2459          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2460          DataTagged*   tmp_1=dynamic_cast<DataTagged*>(arg_1_Z.borrowData());
2461          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2462    
2463          int sampleNo_0,dataPointNo_0;
2464          int numSamples_0 = arg_0_Z.getNumSamples();
2465          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2466          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2467          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2468            int offset_1 = tmp_1->getPointOffset(sampleNo_0,0);
2469            double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2470            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2471              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2472              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2473              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2474              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2475              tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2476            }
2477          }
2478    
2479        }
2480        else if (arg_0_Z.isExpanded()   && arg_1_Z.isExpanded()) {
2481    
2482          // After finding a common function space above the two inputs have the same numSamples and num DPPS
2483          res = Data(0.0, shape0, arg_1_Z.getFunctionSpace(),true); // DataExpanded output
2484          DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2485          DataExpanded* tmp_1=dynamic_cast<DataExpanded*>(arg_1_Z.borrowData());
2486          DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2487    
2488          int sampleNo_0,dataPointNo_0;
2489          int numSamples_0 = arg_0_Z.getNumSamples();
2490          int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2491          #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2492          for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2493            for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2494              int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2495              int offset_1 = tmp_1->getPointOffset(sampleNo_0,dataPointNo_0);
2496              int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2497              double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2498              double *ptr_1 = &((arg_1_Z.getPointDataView().getData())[offset_1]);
2499              double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2500              tensor_binary_operation(size0, ptr_0, ptr_1[0], ptr_2, operation);
2501            }
2502          }
2503    
2504        }
2505        else {
2506          throw DataException("Error - C_TensorBinaryOperation: unknown combination of inputs");
2507        }
2508    
2509      } else {
2510        throw DataException("Error - C_TensorBinaryOperation: arguments have incompatible shapes");
2511      }
2512    
2513      return res;
2514    }
2515    
2516    template <typename UnaryFunction>
2517    Data
2518    C_TensorUnaryOperation(Data const &arg_0,
2519                           UnaryFunction operation)
2520    {
2521      // Interpolate if necessary and find an appropriate function space
2522      Data arg_0_Z = Data(arg_0);
2523    
2524      // Get rank and shape of inputs
2525      int rank0 = arg_0_Z.getDataPointRank();
2526      DataArrayView::ShapeType shape0 = arg_0_Z.getDataPointShape();
2527      int size0 = arg_0_Z.getDataPointSize();
2528    
2529      // Declare output Data object
2530      Data res;
2531    
2532      if (arg_0_Z.isConstant()) {
2533        res = Data(0.0, shape0, arg_0_Z.getFunctionSpace());      // DataConstant output
2534        double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[0]);
2535        double *ptr_2 = &((res.getPointDataView().getData())[0]);
2536        tensor_unary_operation(size0, ptr_0, ptr_2, operation);
2537      }
2538      else if (arg_0_Z.isTagged()) {
2539    
2540        // Borrow DataTagged input from Data object
2541        DataTagged* tmp_0=dynamic_cast<DataTagged*>(arg_0_Z.borrowData());
2542    
2543        // Prepare a DataTagged output 2
2544        res = Data(0.0, shape0, arg_0_Z.getFunctionSpace());   // DataTagged output
2545        res.tag();
2546        DataTagged* tmp_2=dynamic_cast<DataTagged*>(res.borrowData());
2547    
2548        // Get the views
2549        DataArrayView view_0 = tmp_0->getDefaultValue();
2550        DataArrayView view_2 = tmp_2->getDefaultValue();
2551        // Get the pointers to the actual data
2552        double *ptr_0 = &((view_0.getData())[0]);
2553        double *ptr_2 = &((view_2.getData())[0]);
2554        // Compute a result for the default
2555        tensor_unary_operation(size0, ptr_0, ptr_2, operation);
2556        // Compute a result for each tag
2557        const DataTagged::DataMapType& lookup_0=tmp_0->getTagLookup();
2558        DataTagged::DataMapType::const_iterator i; // i->first is a tag, i->second is an offset into memory
2559        for (i=lookup_0.begin();i!=lookup_0.end();i++) {
2560          tmp_2->addTaggedValue(i->first,tmp_2->getDefaultValue());
2561          DataArrayView view_0 = tmp_0->getDataPointByTag(i->first);
2562          DataArrayView view_2 = tmp_2->getDataPointByTag(i->first);
2563          double *ptr_0 = &view_0.getData(0);
2564          double *ptr_2 = &view_2.getData(0);
2565          tensor_unary_operation(size0, ptr_0, ptr_2, operation);
2566        }
2567    
2568      }
2569      else if (arg_0_Z.isExpanded()) {
2570    
2571        res = Data(0.0, shape0, arg_0_Z.getFunctionSpace(),true); // DataExpanded output
2572        DataExpanded* tmp_0=dynamic_cast<DataExpanded*>(arg_0_Z.borrowData());
2573        DataExpanded* tmp_2=dynamic_cast<DataExpanded*>(res.borrowData());
2574    
2575        int sampleNo_0,dataPointNo_0;
2576        int numSamples_0 = arg_0_Z.getNumSamples();
2577        int numDataPointsPerSample_0 = arg_0_Z.getNumDataPointsPerSample();
2578        #pragma omp parallel for private(sampleNo_0,dataPointNo_0) schedule(static)
2579        for (sampleNo_0 = 0; sampleNo_0 < numSamples_0; sampleNo_0++) {
2580          for (dataPointNo_0 = 0; dataPointNo_0 < numDataPointsPerSample_0; dataPointNo_0++) {
2581            int offset_0 = tmp_0->getPointOffset(sampleNo_0,dataPointNo_0);
2582            int offset_2 = tmp_2->getPointOffset(sampleNo_0,dataPointNo_0);
2583            double *ptr_0 = &((arg_0_Z.getPointDataView().getData())[offset_0]);
2584            double *ptr_2 = &((res.getPointDataView().getData())[offset_2]);
2585            tensor_unary_operation(size0, ptr_0, ptr_2, operation);
2586          }
2587        }
2588    
2589      }
2590      else {
2591        throw DataException("Error - C_TensorUnaryOperation: unknown combination of inputs");
2592      }
2593    
2594      return res;
2595    }
2596    
2597  }  }
2598  #endif  #endif

Legend:
Removed from v.1092  
changed lines
  Added in v.1748

  ViewVC Help
Powered by ViewVC 1.1.26