source: trunk/Clp/src/ClpPackedMatrix.hpp @ 1502

Last change on this file since 1502 was 1502, checked in by forrest, 10 years ago

moving sandbox stuff to trunk

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 26.0 KB
Line 
1/* $Id: ClpPackedMatrix.hpp 1502 2010-01-29 14:25:07Z forrest $ */
2// Copyright (C) 2002, International Business Machines
3// Corporation and others.  All Rights Reserved.
4#ifndef ClpPackedMatrix_H
5#define ClpPackedMatrix_H
6
7#include "CoinPragma.hpp"
8
9#include "ClpMatrixBase.hpp"
10
11/** This implements CoinPackedMatrix as derived from ClpMatrixBase.
12
13    It adds a few methods that know about model as well as matrix
14
15    For details see CoinPackedMatrix */
16
17class ClpPackedMatrix2;
18class ClpPackedMatrix3;
19class ClpPackedMatrix : public ClpMatrixBase {
20
21public:
22    /**@name Useful methods */
23    //@{
24    /// Return a complete CoinPackedMatrix
25    virtual CoinPackedMatrix * getPackedMatrix() const {
26        return matrix_;
27    }
28    /** Whether the packed matrix is column major ordered or not. */
29    virtual bool isColOrdered() const {
30        return matrix_->isColOrdered();
31    }
32    /** Number of entries in the packed matrix. */
33    virtual  CoinBigIndex getNumElements() const {
34        return matrix_->getNumElements();
35    }
36    /** Number of columns. */
37    virtual int getNumCols() const {
38        return matrix_->getNumCols();
39    }
40    /** Number of rows. */
41    virtual int getNumRows() const {
42        return matrix_->getNumRows();
43    }
44
45    /** A vector containing the elements in the packed matrix. Note that there
46        might be gaps in this list, entries that do not belong to any
47        major-dimension vector. To get the actual elements one should look at
48        this vector together with vectorStarts and vectorLengths. */
49    virtual const double * getElements() const {
50        return matrix_->getElements();
51    }
52    /// Mutable elements
53    inline double * getMutableElements() const {
54        return matrix_->getMutableElements();
55    }
56    /** A vector containing the minor indices of the elements in the packed
57         matrix. Note that there might be gaps in this list, entries that do not
58         belong to any major-dimension vector. To get the actual elements one
59         should look at this vector together with vectorStarts and
60         vectorLengths. */
61    virtual const int * getIndices() const {
62        return matrix_->getIndices();
63    }
64
65    virtual const CoinBigIndex * getVectorStarts() const {
66        return matrix_->getVectorStarts();
67    }
68    /** The lengths of the major-dimension vectors. */
69    virtual const int * getVectorLengths() const {
70        return matrix_->getVectorLengths();
71    }
72    /** The length of a single major-dimension vector. */
73    virtual int getVectorLength(int index) const {
74        return matrix_->getVectorSize(index);
75    }
76
77    /** Delete the columns whose indices are listed in <code>indDel</code>. */
78    virtual void deleteCols(const int numDel, const int * indDel);
79    /** Delete the rows whose indices are listed in <code>indDel</code>. */
80    virtual void deleteRows(const int numDel, const int * indDel);
81#ifndef CLP_NO_VECTOR
82    /// Append Columns
83    virtual void appendCols(int number, const CoinPackedVectorBase * const * columns);
84    /// Append Rows
85    virtual void appendRows(int number, const CoinPackedVectorBase * const * rows);
86#endif
87    /** Append a set of rows/columns to the end of the matrix. Returns number of errors
88        i.e. if any of the new rows/columns contain an index that's larger than the
89        number of columns-1/rows-1 (if numberOther>0) or duplicates
90        If 0 then rows, 1 if columns */
91    virtual int appendMatrix(int number, int type,
92                             const CoinBigIndex * starts, const int * index,
93                             const double * element, int numberOther = -1);
94    /** Replace the elements of a vector.  The indices remain the same.
95        This is only needed if scaling and a row copy is used.
96        At most the number specified will be replaced.
97        The index is between 0 and major dimension of matrix */
98    virtual void replaceVector(const int index,
99                               const int numReplace, const double * newElements) {
100        matrix_->replaceVector(index, numReplace, newElements);
101    }
102    /** Modify one element of packed matrix.  An element may be added.
103        This works for either ordering If the new element is zero it will be
104        deleted unless keepZero true */
105    virtual void modifyCoefficient(int row, int column, double newElement,
106                                   bool keepZero = false) {
107        matrix_->modifyCoefficient(row, column, newElement, keepZero);
108    }
109    /** Returns a new matrix in reverse order without gaps */
110    virtual ClpMatrixBase * reverseOrderedCopy() const;
111    /// Returns number of elements in column part of basis
112    virtual CoinBigIndex countBasis(const int * whichColumn,
113                                    int & numberColumnBasic);
114    /// Fills in column part of basis
115    virtual void fillBasis(ClpSimplex * model,
116                           const int * whichColumn,
117                           int & numberColumnBasic,
118                           int * row, int * start,
119                           int * rowCount, int * columnCount,
120                           CoinFactorizationDouble * element);
121    /** Creates scales for column copy (rowCopy in model may be modified)
122        returns non-zero if no scaling done */
123    virtual int scale(ClpModel * model, const ClpSimplex * baseModel = NULL) const ;
124    /** Scales rowCopy if column copy scaled
125        Only called if scales already exist */
126    virtual void scaleRowCopy(ClpModel * model) const ;
127    /// Creates scaled column copy if scales exist
128    void createScaledMatrix(ClpSimplex * model) const;
129    /** Realy really scales column copy
130        Only called if scales already exist.
131        Up to user ro delete */
132    virtual ClpMatrixBase * scaledColumnCopy(ClpModel * model) const ;
133    /** Checks if all elements are in valid range.  Can just
134        return true if you are not paranoid.  For Clp I will
135        probably expect no zeros.  Code can modify matrix to get rid of
136        small elements.
137        check bits (can be turned off to save time) :
138        1 - check if matrix has gaps
139        2 - check if zero elements
140        4 - check and compress duplicates
141        8 - report on large and small
142    */
143    virtual bool allElementsInRange(ClpModel * model,
144                                    double smallest, double largest,
145                                    int check = 15);
146    /** Returns largest and smallest elements of both signs.
147        Largest refers to largest absolute value.
148    */
149    virtual void rangeOfElements(double & smallestNegative, double & largestNegative,
150                                 double & smallestPositive, double & largestPositive);
151
152    /** Unpacks a column into an CoinIndexedvector
153     */
154    virtual void unpack(const ClpSimplex * model, CoinIndexedVector * rowArray,
155                        int column) const ;
156    /** Unpacks a column into an CoinIndexedvector
157     ** in packed foramt
158        Note that model is NOT const.  Bounds and objective could
159        be modified if doing column generation (just for this variable) */
160    virtual void unpackPacked(ClpSimplex * model,
161                              CoinIndexedVector * rowArray,
162                              int column) const;
163    /** Adds multiple of a column into an CoinIndexedvector
164        You can use quickAdd to add to vector */
165    virtual void add(const ClpSimplex * model, CoinIndexedVector * rowArray,
166                     int column, double multiplier) const ;
167    /** Adds multiple of a column into an array */
168    virtual void add(const ClpSimplex * model, double * array,
169                     int column, double multiplier) const;
170    /// Allow any parts of a created CoinPackedMatrix to be deleted
171    virtual void releasePackedMatrix() const { }
172    /** Given positive integer weights for each row fills in sum of weights
173        for each column (and slack).
174        Returns weights vector
175    */
176    virtual CoinBigIndex * dubiousWeights(const ClpSimplex * model, int * inputWeights) const;
177    /// Says whether it can do partial pricing
178    virtual bool canDoPartialPricing() const;
179    /// Partial pricing
180    virtual void partialPricing(ClpSimplex * model, double start, double end,
181                                int & bestSequence, int & numberWanted);
182    /// makes sure active columns correct
183    virtual int refresh(ClpSimplex * model);
184    // Really scale matrix
185    virtual void reallyScale(const double * rowScale, const double * columnScale);
186    /** Set the dimensions of the matrix. In effect, append new empty
187        columns/rows to the matrix. A negative number for either dimension
188        means that that dimension doesn't change. Otherwise the new dimensions
189        MUST be at least as large as the current ones otherwise an exception
190        is thrown. */
191    virtual void setDimensions(int numrows, int numcols);
192    //@}
193
194    /**@name Matrix times vector methods */
195    //@{
196    /** Return <code>y + A * scalar *x</code> in <code>y</code>.
197        @pre <code>x</code> must be of size <code>numColumns()</code>
198        @pre <code>y</code> must be of size <code>numRows()</code> */
199    virtual void times(double scalar,
200                       const double * x, double * y) const;
201    /// And for scaling
202    virtual void times(double scalar,
203                       const double * x, double * y,
204                       const double * rowScale,
205                       const double * columnScale) const;
206    /** Return <code>y + x * scalar * A</code> in <code>y</code>.
207        @pre <code>x</code> must be of size <code>numRows()</code>
208        @pre <code>y</code> must be of size <code>numColumns()</code> */
209    virtual void transposeTimes(double scalar,
210                                const double * x, double * y) const;
211    /// And for scaling
212    virtual void transposeTimes(double scalar,
213                                const double * x, double * y,
214                                const double * rowScale,
215                                const double * columnScale,
216                                double * spare = NULL) const;
217    /** Return <code>y - pi * A</code> in <code>y</code>.
218        @pre <code>pi</code> must be of size <code>numRows()</code>
219        @pre <code>y</code> must be of size <code>numColumns()</code>
220    This just does subset (but puts in correct place in y) */
221    void transposeTimesSubset( int number,
222                               const int * which,
223                               const double * pi, double * y,
224                               const double * rowScale,
225                               const double * columnScale,
226                               double * spare = NULL) const;
227    /** Return <code>x * scalar * A + y</code> in <code>z</code>.
228    Can use y as temporary array (will be empty at end)
229    Note - If x packed mode - then z packed mode
230    Squashes small elements and knows about ClpSimplex */
231    virtual void transposeTimes(const ClpSimplex * model, double scalar,
232                                const CoinIndexedVector * x,
233                                CoinIndexedVector * y,
234                                CoinIndexedVector * z) const;
235    /** Return <code>x * scalar * A + y</code> in <code>z</code>.
236    Note - If x packed mode - then z packed mode
237    This does by column and knows no gaps
238    Squashes small elements and knows about ClpSimplex */
239    void transposeTimesByColumn(const ClpSimplex * model, double scalar,
240                                const CoinIndexedVector * x,
241                                CoinIndexedVector * y,
242                                CoinIndexedVector * z) const;
243    /** Return <code>x * scalar * A + y</code> in <code>z</code>.
244    Can use y as temporary array (will be empty at end)
245    Note - If x packed mode - then z packed mode
246    Squashes small elements and knows about ClpSimplex.
247    This version uses row copy*/
248    virtual void transposeTimesByRow(const ClpSimplex * model, double scalar,
249                                     const CoinIndexedVector * x,
250                                     CoinIndexedVector * y,
251                                     CoinIndexedVector * z) const;
252    /** Return <code>x *A</code> in <code>z</code> but
253    just for indices in y.
254    Note - z always packed mode */
255    virtual void subsetTransposeTimes(const ClpSimplex * model,
256                                      const CoinIndexedVector * x,
257                                      const CoinIndexedVector * y,
258                                      CoinIndexedVector * z) const;
259    /** Returns true if can combine transposeTimes and subsetTransposeTimes
260        and if it would be faster */
261    virtual bool canCombine(const ClpSimplex * model,
262                            const CoinIndexedVector * pi) const;
263    /// Updates two arrays for steepest
264    virtual void transposeTimes2(const ClpSimplex * model,
265                                 const CoinIndexedVector * pi1, CoinIndexedVector * dj1,
266                                 const CoinIndexedVector * pi2,
267                                 CoinIndexedVector * spare,
268                                 double referenceIn, double devex,
269                                 // Array for exact devex to say what is in reference framework
270                                 unsigned int * reference,
271                                 double * weights, double scaleFactor);
272    /// Updates second array for steepest and does devex weights
273    virtual void subsetTimes2(const ClpSimplex * model,
274                              CoinIndexedVector * dj1,
275                              const CoinIndexedVector * pi2, CoinIndexedVector * dj2,
276                              double referenceIn, double devex,
277                              // Array for exact devex to say what is in reference framework
278                              unsigned int * reference,
279                              double * weights, double scaleFactor);
280    /// Sets up an effective RHS
281    void useEffectiveRhs(ClpSimplex * model);
282#if COIN_LONG_WORK
283    // For long double versions
284    virtual void times(CoinWorkDouble scalar,
285                       const CoinWorkDouble * x, CoinWorkDouble * y) const ;
286    virtual void transposeTimes(CoinWorkDouble scalar,
287                                const CoinWorkDouble * x, CoinWorkDouble * y) const ;
288#endif
289//@}
290
291    /**@name Other */
292    //@{
293    /// Returns CoinPackedMatrix (non const)
294    inline CoinPackedMatrix * matrix() const {
295        return matrix_;
296    }
297    /** Just sets matrix_ to NULL so it can be used elsewhere.
298        used in GUB
299    */
300    inline void setMatrixNull() {
301        matrix_ = NULL;
302    }
303    /// Say we want special column copy
304    inline void makeSpecialColumnCopy() {
305        flags_ |= 16;
306    }
307    /// Say we don't want special column copy
308    void releaseSpecialColumnCopy();
309    /// Are there zeros?
310    inline bool zeros() const {
311        return ((flags_&1) != 0);
312    }
313    /// Do we want special column copy
314    inline bool wantsSpecialColumnCopy() const {
315        return ((flags_&16) != 0);
316    }
317    /// Flags
318    inline int flags() const {
319        return flags_;
320    }
321    /// Sets flags_ correctly
322    inline void checkGaps() {
323        flags_ = (matrix_->hasGaps()) ? (flags_ | 2) : (flags_ & (~2));
324    }
325    //@}
326
327
328    /**@name Constructors, destructor */
329    //@{
330    /** Default constructor. */
331    ClpPackedMatrix();
332    /** Destructor */
333    virtual ~ClpPackedMatrix();
334    //@}
335
336    /**@name Copy method */
337    //@{
338    /** The copy constructor. */
339    ClpPackedMatrix(const ClpPackedMatrix&);
340    /** The copy constructor from an CoinPackedMatrix. */
341    ClpPackedMatrix(const CoinPackedMatrix&);
342    /** Subset constructor (without gaps).  Duplicates are allowed
343        and order is as given */
344    ClpPackedMatrix (const ClpPackedMatrix & wholeModel,
345                     int numberRows, const int * whichRows,
346                     int numberColumns, const int * whichColumns);
347    ClpPackedMatrix (const CoinPackedMatrix & wholeModel,
348                     int numberRows, const int * whichRows,
349                     int numberColumns, const int * whichColumns);
350
351    /** This takes over ownership (for space reasons) */
352    ClpPackedMatrix(CoinPackedMatrix * matrix);
353
354    ClpPackedMatrix& operator=(const ClpPackedMatrix&);
355    /// Clone
356    virtual ClpMatrixBase * clone() const ;
357    /// Copy contents - resizing if necessary - otherwise re-use memory
358    virtual void copy(const ClpPackedMatrix * from);
359    /** Subset clone (without gaps).  Duplicates are allowed
360        and order is as given */
361    virtual ClpMatrixBase * subsetClone (
362        int numberRows, const int * whichRows,
363        int numberColumns, const int * whichColumns) const ;
364    /// make special row copy
365    void specialRowCopy(ClpSimplex * model, const ClpMatrixBase * rowCopy);
366    /// make special column copy
367    void specialColumnCopy(ClpSimplex * model);
368    /// Correct sequence in and out to give true value
369    virtual void correctSequence(const ClpSimplex * model, int & sequenceIn, int & sequenceOut) ;
370    //@}
371private:
372    /// Meat of transposeTimes by column when not scaled
373    int gutsOfTransposeTimesUnscaled(const double * COIN_RESTRICT pi,
374                                     int * COIN_RESTRICT index,
375                                     double * COIN_RESTRICT array,
376                                     const double tolerance) const;
377    /// Meat of transposeTimes by column when scaled
378    int gutsOfTransposeTimesScaled(const double * COIN_RESTRICT pi,
379                                   const double * COIN_RESTRICT columnScale,
380                                   int * COIN_RESTRICT index,
381                                   double * COIN_RESTRICT array,
382                                   const double tolerance) const;
383    /// Meat of transposeTimes by column when not scaled and skipping
384    int gutsOfTransposeTimesUnscaled(const double * COIN_RESTRICT pi,
385                                     int * COIN_RESTRICT index,
386                                     double * COIN_RESTRICT array,
387                                     const unsigned char * status,
388                                     const double tolerance) const;
389    /** Meat of transposeTimes by column when not scaled and skipping
390        and doing part of dualColumn */
391    int gutsOfTransposeTimesUnscaled(const double * COIN_RESTRICT pi,
392                                     int * COIN_RESTRICT index,
393                                     double * COIN_RESTRICT array,
394                                     const unsigned char * status,
395                                     int * COIN_RESTRICT spareIndex,
396                                     double * COIN_RESTRICT spareArray,
397                                     const double * COIN_RESTRICT reducedCost,
398                                     double & upperTheta,
399                                     double & bestPossible,
400                                     double acceptablePivot,
401                                     double dualTolerance,
402                                     int & numberRemaining,
403                                     const double zeroTolerance) const;
404    /// Meat of transposeTimes by column when scaled and skipping
405    int gutsOfTransposeTimesScaled(const double * COIN_RESTRICT pi,
406                                   const double * COIN_RESTRICT columnScale,
407                                   int * COIN_RESTRICT index,
408                                   double * COIN_RESTRICT array,
409                                   const unsigned char * status,
410                                   const double tolerance) const;
411    /// Meat of transposeTimes by row n > K if packed - returns number nonzero
412    int gutsOfTransposeTimesByRowGEK(const CoinIndexedVector * COIN_RESTRICT piVector,
413                                     int * COIN_RESTRICT index,
414                                     double * COIN_RESTRICT output,
415                                     int numberColumns,
416                                     const double tolerance,
417                                     const double scalar) const;
418    /// Meat of transposeTimes by row n > 2 if packed - returns number nonzero
419    int gutsOfTransposeTimesByRowGE3(const CoinIndexedVector * COIN_RESTRICT piVector,
420                                     int * COIN_RESTRICT index,
421                                     double * COIN_RESTRICT output,
422                                     int * COIN_RESTRICT lookup,
423                                     char * COIN_RESTRICT marked,
424                                     const double tolerance,
425                                     const double scalar) const;
426    /// Meat of transposeTimes by row n == 2 if packed
427    void gutsOfTransposeTimesByRowEQ2(const CoinIndexedVector * piVector, CoinIndexedVector * output,
428                                      CoinIndexedVector * spareVector, const double tolerance, const double scalar) const;
429    /// Meat of transposeTimes by row n == 1 if packed
430    void gutsOfTransposeTimesByRowEQ1(const CoinIndexedVector * piVector, CoinIndexedVector * output,
431                                      const double tolerance, const double scalar) const;
432    /// Gets rid of special copies
433    void clearCopies();
434
435
436protected:
437    /// Check validity
438    void checkFlags(int type) const;
439    /**@name Data members
440       The data members are protected to allow access for derived classes. */
441    //@{
442    /// Data
443    CoinPackedMatrix * matrix_;
444    /// number of active columns (normally same as number of columns)
445    int numberActiveColumns_;
446    /** Flags -
447        1 - has zero elements
448        2 - has gaps
449        4 - has special row copy
450        8 - has special column copy
451        16 - wants special column copy
452    */
453    int flags_;
454    /// Special row copy
455    ClpPackedMatrix2 * rowCopy_;
456    /// Special column copy
457    ClpPackedMatrix3 * columnCopy_;
458    //@}
459};
460#ifdef THREAD
461#include <pthread.h>
462typedef struct {
463    double acceptablePivot;
464    const ClpSimplex * model;
465    double * spare;
466    int * spareIndex;
467    double * arrayTemp;
468    int * indexTemp;
469    int * numberInPtr;
470    double * bestPossiblePtr;
471    double * upperThetaPtr;
472    int * posFreePtr;
473    double * freePivotPtr;
474    int * numberOutPtr;
475    const unsigned short * count;
476    const double * pi;
477    const CoinBigIndex * rowStart;
478    const double * element;
479    const unsigned short * column;
480    int offset;
481    int numberInRowArray;
482    int numberLook;
483} dualColumn0Struct;
484#endif
485class ClpPackedMatrix2 {
486
487public:
488    /**@name Useful methods */
489    //@{
490    /** Return <code>x * -1 * A in <code>z</code>.
491    Note - x packed and z will be packed mode
492    Squashes small elements and knows about ClpSimplex */
493    void transposeTimes(const ClpSimplex * model,
494                        const CoinPackedMatrix * rowCopy,
495                        const CoinIndexedVector * x,
496                        CoinIndexedVector * spareArray,
497                        CoinIndexedVector * z) const;
498    /// Returns true if copy has useful information
499    inline bool usefulInfo() const {
500        return rowStart_ != NULL;
501    }
502    //@}
503
504
505    /**@name Constructors, destructor */
506    //@{
507    /** Default constructor. */
508    ClpPackedMatrix2();
509    /** Constructor from copy. */
510    ClpPackedMatrix2(ClpSimplex * model, const CoinPackedMatrix * rowCopy);
511    /** Destructor */
512    virtual ~ClpPackedMatrix2();
513    //@}
514
515    /**@name Copy method */
516    //@{
517    /** The copy constructor. */
518    ClpPackedMatrix2(const ClpPackedMatrix2&);
519    ClpPackedMatrix2& operator=(const ClpPackedMatrix2&);
520    //@}
521
522
523protected:
524    /**@name Data members
525       The data members are protected to allow access for derived classes. */
526    //@{
527    /// Number of blocks
528    int numberBlocks_;
529    /// Number of rows
530    int numberRows_;
531    /// Column offset for each block (plus one at end)
532    int * offset_;
533    /// Counts of elements in each part of row
534    mutable unsigned short * count_;
535    /// Row starts
536    mutable CoinBigIndex * rowStart_;
537    /// columns within block
538    unsigned short * column_;
539    /// work arrays
540    double * work_;
541#ifdef THREAD
542    pthread_t * threadId_;
543    dualColumn0Struct * info_;
544#endif
545    //@}
546};
547typedef struct {
548    CoinBigIndex startElements_; // point to data
549    int startIndices_; // point to column_
550    int numberInBlock_;
551    int numberPrice_; // at beginning
552    int numberElements_; // number elements per column
553} blockStruct;
554class ClpPackedMatrix3 {
555
556public:
557    /**@name Useful methods */
558    //@{
559    /** Return <code>x * -1 * A in <code>z</code>.
560    Note - x packed and z will be packed mode
561    Squashes small elements and knows about ClpSimplex */
562    void transposeTimes(const ClpSimplex * model,
563                        const double * pi,
564                        CoinIndexedVector * output) const;
565    /// Updates two arrays for steepest
566    void transposeTimes2(const ClpSimplex * model,
567                         const double * pi, CoinIndexedVector * dj1,
568                         const double * piWeight,
569                         double referenceIn, double devex,
570                         // Array for exact devex to say what is in reference framework
571                         unsigned int * reference,
572                         double * weights, double scaleFactor);
573    //@}
574
575
576    /**@name Constructors, destructor */
577    //@{
578    /** Default constructor. */
579    ClpPackedMatrix3();
580    /** Constructor from copy. */
581    ClpPackedMatrix3(ClpSimplex * model, const CoinPackedMatrix * columnCopy);
582    /** Destructor */
583    virtual ~ClpPackedMatrix3();
584    //@}
585
586    /**@name Copy method */
587    //@{
588    /** The copy constructor. */
589    ClpPackedMatrix3(const ClpPackedMatrix3&);
590    ClpPackedMatrix3& operator=(const ClpPackedMatrix3&);
591    //@}
592    /**@name Sort methods */
593    //@{
594    /** Sort blocks */
595    void sortBlocks(const ClpSimplex * model);
596    /// Swap one variable
597    void swapOne(const ClpSimplex * model, const ClpPackedMatrix * matrix,
598                 int iColumn);
599    //@}
600
601
602protected:
603    /**@name Data members
604       The data members are protected to allow access for derived classes. */
605    //@{
606    /// Number of blocks
607    int numberBlocks_;
608    /// Number of columns
609    int numberColumns_;
610    /// Column indices and reverse lookup (within block)
611    int * column_;
612    /// Starts for odd/long vectors
613    CoinBigIndex * start_;
614    /// Rows
615    int * row_;
616    /// Elements
617    double * element_;
618    /// Blocks (ordinary start at 0 and go to first block)
619    blockStruct * block_;
620    //@}
621};
622
623#endif
Note: See TracBrowser for help on using the repository browser.