/[escript]/branches/clazy/escriptcore/src/DataLazy.cpp
ViewVC logotype

Diff of /branches/clazy/escriptcore/src/DataLazy.cpp

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

revision 2066 by jfenwick, Thu Nov 20 05:31:33 2008 UTC revision 2177 by jfenwick, Wed Dec 17 23:51:23 2008 UTC
# Line 28  Line 28 
28  #include "UnaryFuncs.h"     // for escript::fsign  #include "UnaryFuncs.h"     // for escript::fsign
29  #include "Utils.h"  #include "Utils.h"
30    
31    // #define LAZYDEBUG(X) if (privdebug){X;}
32    #define LAZYDEBUG(X)
33    namespace
34    {
35    bool privdebug=false;
36    
37    #define ENABLEDEBUG privdebug=true;
38    #define DISABLEDEBUG privdebug=false;
39    }
40    
41    #define SIZELIMIT
42    // #define SIZELIMIT if ((m_height>7) || (m_children>127)) {cerr << "\n!!!!!!! SIZE LIMIT EXCEEDED " << m_children << ";" << m_height << endl << toString() << endl; resolveToIdentity();}
43    
44    
45  /*  /*
46  How does DataLazy work?  How does DataLazy work?
47  ~~~~~~~~~~~~~~~~~~~~~~~  ~~~~~~~~~~~~~~~~~~~~~~~
# Line 70  The convention that I use, is that the r Line 84  The convention that I use, is that the r
84  For expressions which evaluate to Constant or Tagged, there is a different evaluation method.  For expressions which evaluate to Constant or Tagged, there is a different evaluation method.
85  The collapse method invokes the (non-lazy) operations on the Data class to evaluate the expression.  The collapse method invokes the (non-lazy) operations on the Data class to evaluate the expression.
86    
87  To add a new operator you need to do the following (plus anything I might have forgotten):  To add a new operator you need to do the following (plus anything I might have forgotten - adding a new group for example):
88  1) Add to the ES_optype.  1) Add to the ES_optype.
89  2) determine what opgroup your operation belongs to (X)  2) determine what opgroup your operation belongs to (X)
90  3) add a string for the op to the end of ES_opstrings  3) add a string for the op to the end of ES_opstrings
# Line 96  enum ES_opgroup Line 110  enum ES_opgroup
110     G_IDENTITY,     G_IDENTITY,
111     G_BINARY,        // pointwise operations with two arguments     G_BINARY,        // pointwise operations with two arguments
112     G_UNARY,     // pointwise operations with one argument     G_UNARY,     // pointwise operations with one argument
113       G_UNARY_P,       // pointwise operations with one argument, requiring a parameter
114     G_NP1OUT,        // non-pointwise op with one output     G_NP1OUT,        // non-pointwise op with one output
115       G_NP1OUT_P,      // non-pointwise op with one output requiring a parameter
116     G_TENSORPROD     // general tensor product     G_TENSORPROD     // general tensor product
117  };  };
118    
# Line 108  string ES_opstrings[]={"UNKNOWN","IDENTI Line 124  string ES_opstrings[]={"UNKNOWN","IDENTI
124              "asin","acos","atan","sinh","cosh","tanh","erf",              "asin","acos","atan","sinh","cosh","tanh","erf",
125              "asinh","acosh","atanh",              "asinh","acosh","atanh",
126              "log10","log","sign","abs","neg","pos","exp","sqrt",              "log10","log","sign","abs","neg","pos","exp","sqrt",
127              "1/","where>0","where<0","where>=0","where<=0",              "1/","where>0","where<0","where>=0","where<=0", "where<>0","where=0",
128              "symmetric","nonsymmetric",              "symmetric","nonsymmetric",
129              "prod"};              "prod",
130  int ES_opcount=36;              "transpose", "trace"};
131    int ES_opcount=40;
132  ES_opgroup opgroups[]={G_UNKNOWN,G_IDENTITY,G_BINARY,G_BINARY,G_BINARY,G_BINARY, G_BINARY,  ES_opgroup opgroups[]={G_UNKNOWN,G_IDENTITY,G_BINARY,G_BINARY,G_BINARY,G_BINARY, G_BINARY,
133              G_UNARY,G_UNARY,G_UNARY, //10              G_UNARY,G_UNARY,G_UNARY, //10
134              G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,    // 17              G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,    // 17
135              G_UNARY,G_UNARY,G_UNARY,                    // 20              G_UNARY,G_UNARY,G_UNARY,                    // 20
136              G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,        // 28              G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,    // 28
137              G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY,            // 33              G_UNARY,G_UNARY,G_UNARY,G_UNARY,G_UNARY, G_UNARY_P, G_UNARY_P,      // 35
138              G_NP1OUT,G_NP1OUT,              G_NP1OUT,G_NP1OUT,
139              G_TENSORPROD};              G_TENSORPROD,
140                G_NP1OUT_P, G_NP1OUT_P};
141  inline  inline
142  ES_opgroup  ES_opgroup
143  getOpgroup(ES_optype op)  getOpgroup(ES_optype op)
# Line 177  resultShape(DataAbstract_ptr left, DataA Line 195  resultShape(DataAbstract_ptr left, DataA
195      return left->getShape();      return left->getShape();
196  }  }
197    
198    // return the shape for "op left"
199    
200    DataTypes::ShapeType
201    resultShape(DataAbstract_ptr left, ES_optype op)
202    {
203        switch(op)
204        {
205            case TRANS:
206            return left->getShape();
207        break;
208        case TRACE:
209            return DataTypes::scalarShape;
210        break;
211            default:
212        throw DataException("Programmer error - resultShape(left,op) can't compute shapes for operator "+opToString(op)+".");
213        }
214    }
215    
216  // determine the output shape for the general tensor product operation  // determine the output shape for the general tensor product operation
217  // the additional parameters return information required later for the product  // the additional parameters return information required later for the product
218  // the majority of this code is copy pasted from C_General_Tensor_Product  // the majority of this code is copy pasted from C_General_Tensor_Product
# Line 197  GTPShape(DataAbstract_ptr left, DataAbst Line 233  GTPShape(DataAbstract_ptr left, DataAbst
233    else if (transpose == 2)  { start1 = rank1-axis_offset; }    else if (transpose == 2)  { start1 = rank1-axis_offset; }
234    else              { throw DataException("DataLazy GeneralTensorProduct Constructor: Error - transpose should be 0, 1 or 2"); }    else              { throw DataException("DataLazy GeneralTensorProduct Constructor: Error - transpose should be 0, 1 or 2"); }
235    
236      if (rank0<axis_offset)
237      {
238        throw DataException("DataLazy GeneralTensorProduct Constructor: Error - rank of left < axisoffset");
239      }
240    
241    // Adjust the shapes for transpose    // Adjust the shapes for transpose
242    DataTypes::ShapeType tmpShape0(rank0);    // pre-sizing the vectors rather    DataTypes::ShapeType tmpShape0(rank0);    // pre-sizing the vectors rather
# Line 226  GTPShape(DataAbstract_ptr left, DataAbst Line 266  GTPShape(DataAbstract_ptr left, DataAbst
266       for (int i=0; i<rank0-axis_offset; i++, ++out_index) { shape2[out_index]=tmpShape0[i]; } // First part of arg_0_Z       for (int i=0; i<rank0-axis_offset; i++, ++out_index) { shape2[out_index]=tmpShape0[i]; } // First part of arg_0_Z
267       for (int i=axis_offset; i<rank1; i++, ++out_index)   { shape2[out_index]=tmpShape1[i]; } // Last part of arg_1_Z       for (int i=axis_offset; i<rank1; i++, ++out_index)   { shape2[out_index]=tmpShape1[i]; } // Last part of arg_1_Z
268    }    }
   return shape2;  
 }  
269    
270      if (shape2.size()>ESCRIPT_MAX_DATA_RANK)
271      {
272         ostringstream os;
273         os << "C_GeneralTensorProduct: Error - Attempt to create a rank " << shape2.size() << " object. The maximum rank is " << ESCRIPT_MAX_DATA_RANK << ".";
274         throw DataException(os.str());
275      }
276    
277  // determine the number of points in the result of "left op right"    return shape2;
278  // note that determining the resultLength for G_TENSORPROD is more complex and will not be processed here  }
 // size_t  
 // resultLength(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op)  
 // {  
 //    switch (getOpgroup(op))  
 //    {  
 //    case G_BINARY: return left->getLength();  
 //    case G_UNARY: return left->getLength();  
 //    case G_NP1OUT: return left->getLength();  
 //    default:  
 //  throw DataException("Programmer Error - attempt to getLength() for operator "+opToString(op)+".");  
 //    }  
 // }  
279    
280  // determine the number of samples requires to evaluate an expression combining left and right  // determine the number of samples requires to evaluate an expression combining left and right
281  // NP1OUT needs an extra buffer because we can't write the answers over the top of the input.  // NP1OUT needs an extra buffer because we can't write the answers over the top of the input.
282  // The same goes for G_TENSORPROD  // The same goes for G_TENSORPROD
283    // It might seem that pointwise binary ops (G_BINARY) could be written over the top of the lefths.
284    // This would be true were it not for the possibility that the LHS could be a scalar which needs to be examined
285    // multiple times
286  int  int
287  calcBuffs(const DataLazy_ptr& left, const DataLazy_ptr& right, ES_optype op)  calcBuffs(const DataLazy_ptr& left, const DataLazy_ptr& right, ES_optype op)
288  {  {
289     switch(getOpgroup(op))     switch(getOpgroup(op))
290     {     {
291     case G_IDENTITY: return 1;     case G_IDENTITY: return 1;
292     case G_BINARY: return max(left->getBuffsRequired(),right->getBuffsRequired()+1);     case G_BINARY: return 1+max(left->getBuffsRequired(),right->getBuffsRequired()+1);
293     case G_UNARY: return max(left->getBuffsRequired(),1);     case G_UNARY:
294       case G_UNARY_P: return max(left->getBuffsRequired(),1);
295     case G_NP1OUT: return 1+max(left->getBuffsRequired(),1);     case G_NP1OUT: return 1+max(left->getBuffsRequired(),1);
296       case G_NP1OUT_P: return 1+max(left->getBuffsRequired(),1);
297     case G_TENSORPROD: return 1+max(left->getBuffsRequired(),right->getBuffsRequired()+1);     case G_TENSORPROD: return 1+max(left->getBuffsRequired(),right->getBuffsRequired()+1);
298     default:     default:
299      throw DataException("Programmer Error - attempt to calcBuffs() for operator "+opToString(op)+".");      throw DataException("Programmer Error - attempt to calcBuffs() for operator "+opToString(op)+".");
# Line 281  opToString(ES_optype op) Line 318  opToString(ES_optype op)
318    
319    
320  DataLazy::DataLazy(DataAbstract_ptr p)  DataLazy::DataLazy(DataAbstract_ptr p)
321      : parent(p->getFunctionSpace(),p->getShape()),      : parent(p->getFunctionSpace(),p->getShape())
     m_op(IDENTITY),  
     m_axis_offset(0),  
     m_transpose(0),  
     m_SL(0), m_SM(0), m_SR(0)  
322  {  {
323     if (p->isLazy())     if (p->isLazy())
324     {     {
# Line 296  DataLazy::DataLazy(DataAbstract_ptr p) Line 329  DataLazy::DataLazy(DataAbstract_ptr p)
329     }     }
330     else     else
331     {     {
332      m_id=dynamic_pointer_cast<DataReady>(p);      DataReady_ptr dr=dynamic_pointer_cast<DataReady>(p);
333      if(p->isConstant()) {m_readytype='C';}      makeIdentity(dr);
     else if(p->isExpanded()) {m_readytype='E';}  
     else if (p->isTagged()) {m_readytype='T';}  
     else {throw DataException("Unknown DataReady instance in DataLazy constructor.");}  
334     }     }
335     m_buffsRequired=1;  LAZYDEBUG(cout << "(1)Lazy created with " << m_samplesize << endl;)
    m_samplesize=getNumDPPSample()*getNoValues();  
    m_maxsamplesize=m_samplesize;  
 cout << "(1)Lazy created with " << m_samplesize << endl;  
336  }  }
337    
338    
# Line 337  DataLazy::DataLazy(DataAbstract_ptr left Line 364  DataLazy::DataLazy(DataAbstract_ptr left
364     m_buffsRequired=calcBuffs(m_left, m_right,m_op); // yeah m_right will be null at this point     m_buffsRequired=calcBuffs(m_left, m_right,m_op); // yeah m_right will be null at this point
365     m_samplesize=getNumDPPSample()*getNoValues();     m_samplesize=getNumDPPSample()*getNoValues();
366     m_maxsamplesize=max(m_samplesize,m_left->getMaxSampleSize());     m_maxsamplesize=max(m_samplesize,m_left->getMaxSampleSize());
367       m_children=m_left->m_children+1;
368       m_height=m_left->m_height+1;
369       SIZELIMIT
370  }  }
371    
372    
# Line 398  DataLazy::DataLazy(DataAbstract_ptr left Line 428  DataLazy::DataLazy(DataAbstract_ptr left
428     m_samplesize=getNumDPPSample()*getNoValues();     m_samplesize=getNumDPPSample()*getNoValues();
429     m_maxsamplesize=max(max(m_samplesize,m_right->getMaxSampleSize()),m_left->getMaxSampleSize());       m_maxsamplesize=max(max(m_samplesize,m_right->getMaxSampleSize()),m_left->getMaxSampleSize());  
430     m_buffsRequired=calcBuffs(m_left, m_right,m_op);     m_buffsRequired=calcBuffs(m_left, m_right,m_op);
431  cout << "(3)Lazy created with " << m_samplesize << endl;     m_children=m_left->m_children+m_right->m_children+2;
432       m_height=max(m_left->m_height,m_right->m_height)+1;
433       SIZELIMIT
434    LAZYDEBUG(cout << "(3)Lazy created with " << m_samplesize << endl;)
435  }  }
436    
437  DataLazy::DataLazy(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op, int axis_offset, int transpose)  DataLazy::DataLazy(DataAbstract_ptr left, DataAbstract_ptr right, ES_optype op, int axis_offset, int transpose)
# Line 462  DataLazy::DataLazy(DataAbstract_ptr left Line 495  DataLazy::DataLazy(DataAbstract_ptr left
495     m_samplesize=getNumDPPSample()*getNoValues();     m_samplesize=getNumDPPSample()*getNoValues();
496     m_maxsamplesize=max(max(m_samplesize,m_right->getMaxSampleSize()),m_left->getMaxSampleSize());       m_maxsamplesize=max(max(m_samplesize,m_right->getMaxSampleSize()),m_left->getMaxSampleSize());  
497     m_buffsRequired=calcBuffs(m_left, m_right,m_op);     m_buffsRequired=calcBuffs(m_left, m_right,m_op);
498  cout << "(4)Lazy created with " << m_samplesize << endl;     m_children=m_left->m_children+m_right->m_children+2;
499       m_height=max(m_left->m_height,m_right->m_height)+1;
500       SIZELIMIT
501    LAZYDEBUG(cout << "(4)Lazy created with " << m_samplesize << endl;)
502  }  }
503    
504    
505    DataLazy::DataLazy(DataAbstract_ptr left, ES_optype op, int axis_offset)
506        : parent(left->getFunctionSpace(), resultShape(left,op)),
507        m_op(op),
508        m_axis_offset(axis_offset),
509        m_transpose(0),
510        m_tol(0)
511    {
512       if ((getOpgroup(op)!=G_NP1OUT_P))
513       {
514        throw DataException("Programmer error - constructor DataLazy(left, op, ax) will only process UNARY operations which require parameters.");
515       }
516       DataLazy_ptr lleft;
517       if (!left->isLazy())
518       {
519        lleft=DataLazy_ptr(new DataLazy(left));
520       }
521       else
522       {
523        lleft=dynamic_pointer_cast<DataLazy>(left);
524       }
525       m_readytype=lleft->m_readytype;
526       m_left=lleft;
527       m_buffsRequired=calcBuffs(m_left, m_right,m_op); // yeah m_right will be null at this point
528       m_samplesize=getNumDPPSample()*getNoValues();
529       m_maxsamplesize=max(m_samplesize,m_left->getMaxSampleSize());
530       m_children=m_left->m_children+1;
531       m_height=m_left->m_height+1;
532       SIZELIMIT
533    LAZYDEBUG(cout << "(5)Lazy created with " << m_samplesize << endl;)
534    }
535    
536    DataLazy::DataLazy(DataAbstract_ptr left, ES_optype op, double tol)
537        : parent(left->getFunctionSpace(), left->getShape()),
538        m_op(op),
539        m_axis_offset(0),
540        m_transpose(0),
541        m_tol(tol)
542    {
543       if ((getOpgroup(op)!=G_UNARY_P))
544       {
545        throw DataException("Programmer error - constructor DataLazy(left, op, tol) will only process UNARY operations which require parameters.");
546       }
547       DataLazy_ptr lleft;
548       if (!left->isLazy())
549       {
550        lleft=DataLazy_ptr(new DataLazy(left));
551       }
552       else
553       {
554        lleft=dynamic_pointer_cast<DataLazy>(left);
555       }
556       m_readytype=lleft->m_readytype;
557       m_left=lleft;
558       m_buffsRequired=calcBuffs(m_left, m_right,m_op); // yeah m_right will be null at this point
559       m_samplesize=getNumDPPSample()*getNoValues();
560       m_maxsamplesize=max(m_samplesize,m_left->getMaxSampleSize());
561       m_children=m_left->m_children+1;
562       m_height=m_left->m_height+1;
563       SIZELIMIT
564    LAZYDEBUG(cout << "(6)Lazy created with " << m_samplesize << endl;)
565    }
566    
567  DataLazy::~DataLazy()  DataLazy::~DataLazy()
568  {  {
569  }  }
# Line 602  DataLazy::collapseToReady() Line 700  DataLazy::collapseToReady()
700      case LEZ:      case LEZ:
701      result=left.whereNonPositive();      result=left.whereNonPositive();
702      break;      break;
703        case NEZ:
704        result=left.whereNonZero(m_tol);
705        break;
706        case EZ:
707        result=left.whereZero(m_tol);
708        break;
709      case SYM:      case SYM:
710      result=left.symmetric();      result=left.symmetric();
711      break;      break;
# Line 611  DataLazy::collapseToReady() Line 715  DataLazy::collapseToReady()
715      case PROD:      case PROD:
716      result=C_GeneralTensorProduct(left,right,m_axis_offset, m_transpose);      result=C_GeneralTensorProduct(left,right,m_axis_offset, m_transpose);
717      break;      break;
718        case TRANS:
719        result=left.transpose(m_axis_offset);
720        break;
721        case TRACE:
722        result=left.trace(m_axis_offset);
723        break;
724      default:      default:
725      throw DataException("Programmer error - collapseToReady does not know how to resolve operator "+opToString(m_op)+".");      throw DataException("Programmer error - collapseToReady does not know how to resolve operator "+opToString(m_op)+".");
726    }    }
# Line 762  DataLazy::resolveUnary(ValueType& v, siz Line 872  DataLazy::resolveUnary(ValueType& v, siz
872      case LEZ:      case LEZ:
873      tensor_unary_operation(m_samplesize, left, result, bind2nd(less_equal<double>(),0.0));      tensor_unary_operation(m_samplesize, left, result, bind2nd(less_equal<double>(),0.0));
874      break;      break;
875    // There are actually G_UNARY_P but I don't see a compelling reason to treat them differently
876        case NEZ:
877        tensor_unary_operation(m_samplesize, left, result, bind2nd(AbsGT(),m_tol));
878        break;
879        case EZ:
880        tensor_unary_operation(m_samplesize, left, result, bind2nd(AbsLTE(),m_tol));
881        break;
882    
883      default:      default:
884      throw DataException("Programmer error - resolveUnary can not resolve operator "+opToString(m_op)+".");      throw DataException("Programmer error - resolveUnary can not resolve operator "+opToString(m_op)+".");
# Line 770  DataLazy::resolveUnary(ValueType& v, siz Line 887  DataLazy::resolveUnary(ValueType& v, siz
887  }  }
888    
889    
890    
891    
892    
893    
894  /*  /*
895    \brief Compute the value of the expression (unary operation) for the given sample.    \brief Compute the value of the expression (unary operation) for the given sample.
896    \return Vector which stores the value of the subexpression for the given sample.    \return Vector which stores the value of the subexpression for the given sample.
# Line 794  DataLazy::resolveNP1OUT(ValueType& v, si Line 915  DataLazy::resolveNP1OUT(ValueType& v, si
915    }    }
916      // since we can't write the result over the input, we need a result offset further along      // since we can't write the result over the input, we need a result offset further along
917    size_t subroffset=roffset+m_samplesize;    size_t subroffset=roffset+m_samplesize;
918    const ValueType* vleft=m_left->resolveSample(v,offset,sampleNo,subroffset);  LAZYDEBUG(cerr << "subroffset=" << subroffset << endl;)
919      const ValueType* vleft=m_left->resolveSample(v,offset+m_left->m_samplesize,sampleNo,subroffset);
920    roffset=offset;    roffset=offset;
921      size_t loop=0;
922      size_t numsteps=(m_readytype=='E')?getNumDPPSample():1;
923      size_t step=getNoValues();
924    switch (m_op)    switch (m_op)
925    {    {
926      case SYM:      case SYM:
927      DataMaths::symmetric(*vleft,m_left->getShape(),subroffset, v, getShape(), offset);      for (loop=0;loop<numsteps;++loop)
928        {
929            DataMaths::symmetric(*vleft,m_left->getShape(),subroffset, v, getShape(), offset);
930            subroffset+=step;
931            offset+=step;
932        }
933      break;      break;
934      case NSYM:      case NSYM:
935      DataMaths::nonsymmetric(*vleft,m_left->getShape(),subroffset, v, getShape(), offset);      for (loop=0;loop<numsteps;++loop)
936        {
937            DataMaths::nonsymmetric(*vleft,m_left->getShape(),subroffset, v, getShape(), offset);
938            subroffset+=step;
939            offset+=step;
940        }
941      break;      break;
942      default:      default:
943      throw DataException("Programmer error - resolveNP1OUT can not resolve operator "+opToString(m_op)+".");      throw DataException("Programmer error - resolveNP1OUT can not resolve operator "+opToString(m_op)+".");
# Line 810  DataLazy::resolveNP1OUT(ValueType& v, si Line 945  DataLazy::resolveNP1OUT(ValueType& v, si
945    return &v;    return &v;
946  }  }
947    
948    /*
949      \brief Compute the value of the expression (unary operation) for the given sample.
950      \return Vector which stores the value of the subexpression for the given sample.
951      \param v A vector to store intermediate results.
952      \param offset Index in v to begin storing results.
953      \param sampleNo Sample number to evaluate.
954      \param roffset (output parameter) the offset in the return vector where the result begins.
955    
956      The return value will be an existing vector so do not deallocate it.
957      If the result is stored in v it should be stored at the offset given.
958      Everything from offset to the end of v should be considered available for this method to use.
959    */
960    DataTypes::ValueType*
961    DataLazy::resolveNP1OUT_P(ValueType& v, size_t offset, int sampleNo, size_t& roffset) const
962    {
963        // we assume that any collapsing has been done before we get here
964        // since we only have one argument we don't need to think about only
965        // processing single points.
966      if (m_readytype!='E')
967      {
968        throw DataException("Programmer error - resolveNP1OUT_P should only be called on expanded Data.");
969      }
970        // since we can't write the result over the input, we need a result offset further along
971      size_t subroffset;
972      const ValueType* vleft=m_left->resolveSample(v,offset+m_left->m_samplesize,sampleNo,subroffset);
973    LAZYDEBUG(cerr << "srcsamplesize=" << offset+m_left->m_samplesize << " beg=" << subroffset << endl;)
974    LAZYDEBUG(cerr << "Offset for 5800=" << getPointOffset(5800/getNumDPPSample(),5800%getNumDPPSample()) << endl;)
975      roffset=offset;
976      size_t loop=0;
977      size_t numsteps=(m_readytype=='E')?getNumDPPSample():1;
978      size_t outstep=getNoValues();
979      size_t instep=m_left->getNoValues();
980    LAZYDEBUG(cerr << "instep=" << instep << " outstep=" << outstep<< " numsteps=" << numsteps << endl;)
981      switch (m_op)
982      {
983        case TRACE:
984        for (loop=0;loop<numsteps;++loop)
985        {
986    size_t zz=sampleNo*getNumDPPSample()+loop;
987    if (zz==5800)
988    {
989    LAZYDEBUG(cerr << "point=" <<  zz<< endl;)
990    LAZYDEBUG(cerr << "Input to  trace=" << DataTypes::pointToString(*vleft,m_left->getShape(),subroffset,"") << endl;)
991    LAZYDEBUG(cerr << "Offset for point=" << getPointOffset(5800/getNumDPPSample(),5800%getNumDPPSample()) << " vs ";)
992    LAZYDEBUG(cerr << subroffset << endl;)
993    LAZYDEBUG(cerr << "output=" << offset << endl;)
994    }
995                DataMaths::trace(*vleft,m_left->getShape(),subroffset, v ,getShape(),offset,m_axis_offset);
996    if (zz==5800)
997    {
998    LAZYDEBUG(cerr << "Result of trace=" << DataTypes::pointToString(v,getShape(),offset,"") << endl;)
999    }
1000            subroffset+=instep;
1001            offset+=outstep;
1002        }
1003        break;
1004        case TRANS:
1005        for (loop=0;loop<numsteps;++loop)
1006        {
1007                DataMaths::transpose(*vleft,m_left->getShape(),subroffset, v,getShape(),offset,m_axis_offset);
1008            subroffset+=instep;
1009            offset+=outstep;
1010        }
1011        break;
1012        default:
1013        throw DataException("Programmer error - resolveNP1OUTP can not resolve operator "+opToString(m_op)+".");
1014      }
1015      return &v;
1016    }
1017    
1018    
1019  #define PROC_OP(TYPE,X)                               \  #define PROC_OP(TYPE,X)                               \
1020      for (int i=0;i<steps;++i,resultp+=resultStep) \      for (int j=0;j<onumsteps;++j)\
1021      { \      {\
1022         tensor_binary_operation< TYPE >(chunksize, &((*left)[lroffset]), &((*right)[rroffset]), resultp, X); \        for (int i=0;i<numsteps;++i,resultp+=resultStep) \
1023         lroffset+=leftStep; \        { \
1024         rroffset+=rightStep; \  LAZYDEBUG(cout << "[left,right]=[" << lroffset << "," << rroffset << "]" << endl;)\
1025    LAZYDEBUG(cout << "{left,right}={" << (*left)[lroffset] << "," << (*right)[rroffset] << "}\n";)\
1026             tensor_binary_operation< TYPE >(chunksize, &((*left)[lroffset]), &((*right)[rroffset]), resultp, X); \
1027    LAZYDEBUG(cout << " result=      " << resultp[0] << endl;) \
1028             lroffset+=leftstep; \
1029             rroffset+=rightstep; \
1030          }\
1031          lroffset+=oleftstep;\
1032          rroffset+=orightstep;\
1033      }      }
1034    
1035  /*  /*
# Line 845  DataLazy::resolveNP1OUT(ValueType& v, si Line 1056  DataLazy::resolveNP1OUT(ValueType& v, si
1056  DataTypes::ValueType*  DataTypes::ValueType*
1057  DataLazy::resolveBinary(ValueType& v,  size_t offset, int sampleNo, size_t& roffset) const  DataLazy::resolveBinary(ValueType& v,  size_t offset, int sampleNo, size_t& roffset) const
1058  {  {
1059  cout << "Resolve binary: " << toString() << endl;  LAZYDEBUG(cout << "Resolve binary: " << toString() << endl;)
1060    
1061    size_t lroffset=0, rroffset=0;    // offsets in the left and right result vectors    size_t lroffset=0, rroffset=0;    // offsets in the left and right result vectors
1062      // first work out which of the children are expanded      // first work out which of the children are expanded
1063    bool leftExp=(m_left->m_readytype=='E');    bool leftExp=(m_left->m_readytype=='E');
1064    bool rightExp=(m_right->m_readytype=='E');    bool rightExp=(m_right->m_readytype=='E');
1065    bool bigloops=((leftExp && rightExp) || (!leftExp && !rightExp)); // is processing in single step?    if (!leftExp && !rightExp)
1066    int steps=(bigloops?1:getNumDPPSample());    {
1067    size_t chunksize=(bigloops? m_samplesize : getNoValues());    // if bigloops, pretend the whole sample is a datapoint      throw DataException("Programmer Error - please use collapse if neither argument has type 'E'.");
1068    if (m_left->getRank()!=m_right->getRank())    // need to deal with scalar * ? ops    }
1069    {    bool leftScalar=(m_left->getRank()==0);
1070      EsysAssert((m_left->getRank()==0) || (m_right->getRank()==0), "Error - Ranks must match unless one is 0.");    bool rightScalar=(m_right->getRank()==0);
1071      steps=getNumDPPSample()*max(m_left->getNoValues(),m_right->getNoValues());    if ((m_left->getRank()!=m_right->getRank()) && (!leftScalar && !rightScalar))
1072      chunksize=1;    // for scalar    {
1073    }          throw DataException("resolveBinary - ranks of arguments must match unless one of them is scalar.");
1074    int leftStep=((leftExp && !rightExp)? m_right->getNoValues() : 0);    }
1075    int rightStep=((rightExp && !leftExp)? m_left->getNoValues() : 0);    size_t leftsize=m_left->getNoValues();
1076    int resultStep=max(leftStep,rightStep);   // only one (at most) should be !=0    size_t rightsize=m_right->getNoValues();
1077      size_t chunksize=1;           // how many doubles will be processed in one go
1078      int leftstep=0;       // how far should the left offset advance after each step
1079      int rightstep=0;
1080      int numsteps=0;       // total number of steps for the inner loop
1081      int oleftstep=0;  // the o variables refer to the outer loop
1082      int orightstep=0; // The outer loop is only required in cases where there is an extended scalar
1083      int onumsteps=1;
1084      
1085      bool LES=(leftExp && leftScalar); // Left is an expanded scalar
1086      bool RES=(rightExp && rightScalar);
1087      bool LS=(!leftExp && leftScalar); // left is a single scalar
1088      bool RS=(!rightExp && rightScalar);
1089      bool LN=(!leftExp && !leftScalar);    // left is a single non-scalar
1090      bool RN=(!rightExp && !rightScalar);
1091      bool LEN=(leftExp && !leftScalar);    // left is an expanded non-scalar
1092      bool REN=(rightExp && !rightScalar);
1093    
1094      if ((LES && RES) || (LEN && REN)) // both are Expanded scalars or both are expanded non-scalars
1095      {
1096        chunksize=m_left->getNumDPPSample()*leftsize;
1097        leftstep=0;
1098        rightstep=0;
1099        numsteps=1;
1100      }
1101      else if (LES || RES)
1102      {
1103        chunksize=1;
1104        if (LES)        // left is an expanded scalar
1105        {
1106            if (RS)
1107            {
1108               leftstep=1;
1109               rightstep=0;
1110               numsteps=m_left->getNumDPPSample();
1111            }
1112            else        // RN or REN
1113            {
1114               leftstep=0;
1115               oleftstep=1;
1116               rightstep=1;
1117               orightstep=(RN ? -(int)rightsize : 0);
1118               numsteps=rightsize;
1119               onumsteps=m_left->getNumDPPSample();
1120            }
1121        }
1122        else        // right is an expanded scalar
1123        {
1124            if (LS)
1125            {
1126               rightstep=1;
1127               leftstep=0;
1128               numsteps=m_right->getNumDPPSample();
1129            }
1130            else
1131            {
1132               rightstep=0;
1133               orightstep=1;
1134               leftstep=1;
1135               oleftstep=(LN ? -(int)leftsize : 0);
1136               numsteps=leftsize;
1137               onumsteps=m_right->getNumDPPSample();
1138            }
1139        }
1140      }
1141      else  // this leaves (LEN, RS), (LEN, RN) and their transposes
1142      {
1143        if (LEN)    // and Right will be a single value
1144        {
1145            chunksize=rightsize;
1146            leftstep=rightsize;
1147            rightstep=0;
1148            numsteps=m_left->getNumDPPSample();
1149            if (RS)
1150            {
1151               numsteps*=leftsize;
1152            }
1153        }
1154        else    // REN
1155        {
1156            chunksize=leftsize;
1157            rightstep=leftsize;
1158            leftstep=0;
1159            numsteps=m_right->getNumDPPSample();
1160            if (LS)
1161            {
1162               numsteps*=rightsize;
1163            }
1164        }
1165      }
1166    
1167      int resultStep=max(leftstep,rightstep);   // only one (at most) should be !=0
1168      // Get the values of sub-expressions      // Get the values of sub-expressions
1169    const ValueType* left=m_left->resolveSample(v,offset,sampleNo,lroffset);    const ValueType* left=m_left->resolveSample(v,offset+getMaxSampleSize(),sampleNo,lroffset);   // see note on
1170    const ValueType* right=m_right->resolveSample(v,offset+m_samplesize,sampleNo,rroffset); // Note      // calcBufss for why we can't put offset as the 2nd param above
1171      const ValueType* right=m_right->resolveSample(v,offset+2*getMaxSampleSize(),sampleNo,rroffset); // Note
1172      // the right child starts further along.      // the right child starts further along.
1173    LAZYDEBUG(cout << "Post sub calls in " << toString() << endl;)
1174    LAZYDEBUG(cout << "shapes=" << DataTypes::shapeToString(m_left->getShape()) << "," << DataTypes::shapeToString(m_right->getShape()) << endl;)
1175    LAZYDEBUG(cout << "chunksize=" << chunksize << endl << "leftstep=" << leftstep << " rightstep=" << rightstep;)
1176    LAZYDEBUG(cout << " numsteps=" << numsteps << endl << "oleftstep=" << oleftstep << " orightstep=" << orightstep;)
1177    LAZYDEBUG(cout << "onumsteps=" << onumsteps << endl;)
1178    LAZYDEBUG(cout << " DPPS=" << m_left->getNumDPPSample() << "," <<m_right->getNumDPPSample() << endl;)
1179    LAZYDEBUG(cout << "" << LS << RS << LN << RN << LES << RES <<LEN << REN <<   endl;)
1180    double* resultp=&(v[offset]);     // results are stored at the vector offset we recieved    double* resultp=&(v[offset]);     // results are stored at the vector offset we recieved
1181    switch(m_op)    switch(m_op)
1182    {    {
# Line 893  cout << "Resolve binary: " << toString() Line 1203  cout << "Resolve binary: " << toString()
1203  }  }
1204    
1205    
1206    
1207  /*  /*
1208    \brief Compute the value of the expression (tensor product) for the given sample.    \brief Compute the value of the expression (tensor product) for the given sample.
1209    \return Vector which stores the value of the subexpression for the given sample.    \return Vector which stores the value of the subexpression for the given sample.
# Line 911  cout << "Resolve binary: " << toString() Line 1222  cout << "Resolve binary: " << toString()
1222  DataTypes::ValueType*  DataTypes::ValueType*
1223  DataLazy::resolveTProd(ValueType& v,  size_t offset, int sampleNo, size_t& roffset) const  DataLazy::resolveTProd(ValueType& v,  size_t offset, int sampleNo, size_t& roffset) const
1224  {  {
1225  cout << "Resolve TensorProduct: " << toString() << endl;  LAZYDEBUG(cout << "Resolve TensorProduct: " << toString() << endl;)
1226    
1227    size_t lroffset=0, rroffset=0;    // offsets in the left and right result vectors    size_t lroffset=0, rroffset=0;    // offsets in the left and right result vectors
1228      // first work out which of the children are expanded      // first work out which of the children are expanded
# Line 922  cout << "Resolve TensorProduct: " << toS Line 1233  cout << "Resolve TensorProduct: " << toS
1233    int rightStep=((rightExp && !leftExp)? m_left->getNoValues() : 0);    int rightStep=((rightExp && !leftExp)? m_left->getNoValues() : 0);
1234    int resultStep=max(leftStep,rightStep);   // only one (at most) should be !=0    int resultStep=max(leftStep,rightStep);   // only one (at most) should be !=0
1235      // Get the values of sub-expressions (leave a gap of one sample for the result).      // Get the values of sub-expressions (leave a gap of one sample for the result).
1236    const ValueType* left=m_left->resolveSample(v,offset+m_samplesize,sampleNo,lroffset);    int gap=offset+m_left->getMaxSampleSize();    // actually only needs to be m_left->m_samplesize
1237    const ValueType* right=m_right->resolveSample(v,offset+2*m_samplesize,sampleNo,rroffset);    const ValueType* left=m_left->resolveSample(v,gap,sampleNo,lroffset);
1238      gap+=m_right->getMaxSampleSize();
1239      const ValueType* right=m_right->resolveSample(v,gap,sampleNo,rroffset);
1240    LAZYDEBUG(cout << "Post sub calls: " << toString() << endl;)
1241    LAZYDEBUG(cout << "LeftExp=" << leftExp << " rightExp=" << rightExp << endl;)
1242    LAZYDEBUG(cout << "LeftR=" << m_left->getRank() << " rightExp=" << m_right->getRank() << endl;)
1243    LAZYDEBUG(cout << "LeftSize=" << m_left->getNoValues() << " RightSize=" << m_right->getNoValues() << endl;)
1244    LAZYDEBUG(cout << "m_samplesize=" << m_samplesize << endl;)
1245    LAZYDEBUG(cout << "outputshape=" << DataTypes::shapeToString(getShape()) << endl;)
1246    double* resultp=&(v[offset]);     // results are stored at the vector offset we recieved    double* resultp=&(v[offset]);     // results are stored at the vector offset we recieved
1247    switch(m_op)    switch(m_op)
1248    {    {
1249      case PROD:      case PROD:
1250      for (int i=0;i<steps;++i,resultp+=resultStep)      for (int i=0;i<steps;++i,resultp+=resultStep)
1251      {      {
1252    LAZYDEBUG(cout << "lroffset=" << lroffset << "rroffset=" << rroffset << endl;)
1253    LAZYDEBUG(cout << "l*=" << left << " r*=" << right << endl;)
1254    LAZYDEBUG(cout << "m_SL=" << m_SL << " m_SM=" << m_SM << " m_SR=" << m_SR << endl;)
1255            const double *ptr_0 = &((*left)[lroffset]);            const double *ptr_0 = &((*left)[lroffset]);
1256            const double *ptr_1 = &((*right)[rroffset]);            const double *ptr_1 = &((*right)[rroffset]);
1257    LAZYDEBUG(cout << DataTypes::pointToString(*left, m_left->getShape(),lroffset,"LEFT") << endl;)
1258    LAZYDEBUG(cout << DataTypes::pointToString(*right,m_right->getShape(),rroffset, "RIGHT") << endl;)
1259            matrix_matrix_product(m_SL, m_SM, m_SR, ptr_0, ptr_1, resultp, m_transpose);            matrix_matrix_product(m_SL, m_SM, m_SR, ptr_0, ptr_1, resultp, m_transpose);
1260        lroffset+=leftStep;        lroffset+=leftStep;
1261        rroffset+=rightStep;        rroffset+=rightStep;
# Line 965  cout << "Resolve TensorProduct: " << toS Line 1289  cout << "Resolve TensorProduct: " << toS
1289  const DataTypes::ValueType*  const DataTypes::ValueType*
1290  DataLazy::resolveSample(ValueType& v, size_t offset, int sampleNo, size_t& roffset)  DataLazy::resolveSample(ValueType& v, size_t offset, int sampleNo, size_t& roffset)
1291  {  {
1292  cout << "Resolve sample " << toString() << endl;  LAZYDEBUG(cout << "Resolve sample " << toString() << endl;)
1293      // collapse so we have a 'E' node or an IDENTITY for some other type      // collapse so we have a 'E' node or an IDENTITY for some other type
1294    if (m_readytype!='E' && m_op!=IDENTITY)    if (m_readytype!='E' && m_op!=IDENTITY)
1295    {    {
# Line 977  cout << "Resolve sample " << toString() Line 1301  cout << "Resolve sample " << toString()
1301      if (m_readytype=='C')      if (m_readytype=='C')
1302      {      {
1303      roffset=0;      roffset=0;
1304    LAZYDEBUG(cout << "Finish  sample " << toString() << endl;)
1305      return &(vec);      return &(vec);
1306      }      }
1307      roffset=m_id->getPointOffset(sampleNo, 0);      roffset=m_id->getPointOffset(sampleNo, 0);
1308    LAZYDEBUG(cout << "Finish  sample " << toString() << endl;)
1309      return &(vec);      return &(vec);
1310    }    }
1311    if (m_readytype!='E')    if (m_readytype!='E')
# Line 988  cout << "Resolve sample " << toString() Line 1314  cout << "Resolve sample " << toString()
1314    }    }
1315    switch (getOpgroup(m_op))    switch (getOpgroup(m_op))
1316    {    {
1317    case G_UNARY: return resolveUnary(v, offset,sampleNo,roffset);    case G_UNARY:
1318      case G_UNARY_P: return resolveUnary(v, offset,sampleNo,roffset);
1319    case G_BINARY: return resolveBinary(v, offset,sampleNo,roffset);    case G_BINARY: return resolveBinary(v, offset,sampleNo,roffset);
1320    case G_NP1OUT: return resolveNP1OUT(v, offset, sampleNo,roffset);    case G_NP1OUT: return resolveNP1OUT(v, offset, sampleNo,roffset);
1321      case G_NP1OUT_P: return resolveNP1OUT_P(v, offset, sampleNo,roffset);
1322    case G_TENSORPROD: return resolveTProd(v,offset, sampleNo,roffset);    case G_TENSORPROD: return resolveTProd(v,offset, sampleNo,roffset);
1323    default:    default:
1324      throw DataException("Programmer Error - resolveSample does not know how to process "+opToString(m_op)+".");      throw DataException("Programmer Error - resolveSample does not know how to process "+opToString(m_op)+".");
1325    }    }
1326    
1327  }  }
1328    
1329    // This needs to do the work of the idenity constructor
1330    void
1331    DataLazy::resolveToIdentity()
1332    {
1333       if (m_op==IDENTITY)
1334        return;
1335       DataReady_ptr p=resolve();
1336       makeIdentity(p);
1337    }
1338    
1339    void DataLazy::makeIdentity(const DataReady_ptr& p)
1340    {
1341       m_op=IDENTITY;
1342       m_axis_offset=0;
1343       m_transpose=0;
1344       m_SL=m_SM=m_SR=0;
1345       m_children=m_height=0;
1346       m_id=p;
1347       if(p->isConstant()) {m_readytype='C';}
1348       else if(p->isExpanded()) {m_readytype='E';}
1349       else if (p->isTagged()) {m_readytype='T';}
1350       else {throw DataException("Unknown DataReady instance in convertToIdentity constructor.");}
1351       m_buffsRequired=1;
1352       m_samplesize=p->getNumDPPSample()*p->getNoValues();
1353       m_maxsamplesize=m_samplesize;
1354       m_left.reset();
1355       m_right.reset();
1356    }
1357    
1358  // To simplify the memory management, all threads operate on one large vector, rather than one each.  // To simplify the memory management, all threads operate on one large vector, rather than one each.
1359  // Each sample is evaluated independently and copied into the result DataExpanded.  // Each sample is evaluated independently and copied into the result DataExpanded.
# Line 1004  DataReady_ptr Line 1361  DataReady_ptr
1361  DataLazy::resolve()  DataLazy::resolve()
1362  {  {
1363    
1364  cout << "Sample size=" << m_samplesize << endl;  LAZYDEBUG(cout << "Sample size=" << m_samplesize << endl;)
1365  cout << "Buffers=" << m_buffsRequired << endl;  LAZYDEBUG(cout << "Buffers=" << m_buffsRequired << endl;)
1366    
1367    if (m_readytype!='E')     // if the whole sub-expression is Constant or Tagged, then evaluate it normally    if (m_readytype!='E')     // if the whole sub-expression is Constant or Tagged, then evaluate it normally
1368    {    {
# Line 1021  cout << "Buffers=" << m_buffsRequired << Line 1378  cout << "Buffers=" << m_buffsRequired <<
1378    int numthreads=1;    int numthreads=1;
1379  #ifdef _OPENMP  #ifdef _OPENMP
1380    numthreads=getNumberOfThreads();    numthreads=getNumberOfThreads();
   int threadnum=0;  
1381  #endif  #endif
1382    ValueType v(numthreads*threadbuffersize);    ValueType v(numthreads*threadbuffersize);
1383  cout << "Buffer created with size=" << v.size() << endl;  LAZYDEBUG(cout << "Buffer created with size=" << v.size() << endl;)
1384    DataExpanded* result=new DataExpanded(getFunctionSpace(),getShape(),  ValueType(getNoValues()));    DataExpanded* result=new DataExpanded(getFunctionSpace(),getShape(),  ValueType(getNoValues()));
1385    ValueType& resvec=result->getVector();    ValueType& resvec=result->getVector();
1386    DataReady_ptr resptr=DataReady_ptr(result);    DataReady_ptr resptr=DataReady_ptr(result);
# Line 1033  cout << "Buffer created with size=" << v Line 1389  cout << "Buffer created with size=" << v
1389    int totalsamples=getNumSamples();    int totalsamples=getNumSamples();
1390    const ValueType* res=0;   // Vector storing the answer    const ValueType* res=0;   // Vector storing the answer
1391    size_t resoffset=0;       // where in the vector to find the answer    size_t resoffset=0;       // where in the vector to find the answer
1392    #pragma omp parallel for private(sample,resoffset,outoffset,threadnum,res) schedule(static)  LAZYDEBUG(cout << "Total number of samples=" <<totalsamples << endl;)
1393      #pragma omp parallel for private(sample,resoffset,outoffset,res) schedule(static)
1394    for (sample=0;sample<totalsamples;++sample)    for (sample=0;sample<totalsamples;++sample)
1395    {    {
1396  cout << "################################# " << sample << endl;        if (sample==0)  {ENABLEDEBUG}
1397    
1398    //       if (sample==5800/getNumDPPSample())  {ENABLEDEBUG}
1399    LAZYDEBUG(cout << "################################# " << sample << endl;)
1400  #ifdef _OPENMP  #ifdef _OPENMP
1401      res=resolveSample(v,threadbuffersize*omp_get_thread_num(),sample,resoffset);      res=resolveSample(v,threadbuffersize*omp_get_thread_num(),sample,resoffset);
1402  #else  #else
1403      res=resolveSample(v,0,sample,resoffset);   // res would normally be v, but not if its a single IDENTITY op.      res=resolveSample(v,0,sample,resoffset);   // res would normally be v, but not if its a single IDENTITY op.
1404  #endif  #endif
1405  cerr << "-------------------------------- " << endl;  LAZYDEBUG(cerr << "-------------------------------- " << endl;)
1406    LAZYDEBUG(cerr<< "Copying sample#" << sample << endl;)
1407      outoffset=result->getPointOffset(sample,0);      outoffset=result->getPointOffset(sample,0);
1408  cerr << "offset=" << outoffset << endl;  LAZYDEBUG(cerr << "offset=" << outoffset << " from offset=" << resoffset << " " << m_samplesize << " doubles" << endl;)
1409      for (unsigned int i=0;i<m_samplesize;++i,++outoffset,++resoffset)   // copy values into the output vector      for (unsigned int i=0;i<m_samplesize;++i,++outoffset,++resoffset)   // copy values into the output vector
1410      {      {
1411    LAZYDEBUG(cerr << "outoffset=" << outoffset << " resoffset=" << resoffset << " " << (*res)[resoffset]<< endl;)
1412      resvec[outoffset]=(*res)[resoffset];      resvec[outoffset]=(*res)[resoffset];
1413      }      }
1414  cerr << "*********************************" << endl;  LAZYDEBUG(cerr << DataTypes::pointToString(resvec,getShape(),outoffset-m_samplesize+DataTypes::noValues(getShape()),"Final result:") << endl;)
1415    LAZYDEBUG(cerr << "*********************************" << endl;)
1416        DISABLEDEBUG
1417    }    }
1418    return resptr;    return resptr;
1419  }  }
# Line 1067  DataLazy::toString() const Line 1431  DataLazy::toString() const
1431  void  void
1432  DataLazy::intoString(ostringstream& oss) const  DataLazy::intoString(ostringstream& oss) const
1433  {  {
1434      oss << "[" << m_children <<";"<<m_height <<"]";
1435    switch (getOpgroup(m_op))    switch (getOpgroup(m_op))
1436    {    {
1437    case G_IDENTITY:    case G_IDENTITY:
# Line 1096  DataLazy::intoString(ostringstream& oss) Line 1461  DataLazy::intoString(ostringstream& oss)
1461      oss << ')';      oss << ')';
1462      break;      break;
1463    case G_UNARY:    case G_UNARY:
1464      case G_UNARY_P:
1465    case G_NP1OUT:    case G_NP1OUT:
1466      case G_NP1OUT_P:
1467      oss << opToString(m_op) << '(';      oss << opToString(m_op) << '(';
1468      m_left->intoString(oss);      m_left->intoString(oss);
1469      oss << ')';      oss << ')';

Legend:
Removed from v.2066  
changed lines
  Added in v.2177

  ViewVC Help
Powered by ViewVC 1.1.26