1 #ifndef SimTK_SimTKCOMMON_MATRIX_HELPER_REP_VECTOR_H_
2 #define SimTK_SimTKCOMMON_MATRIX_HELPER_REP_VECTOR_H_
3 
4 /* -------------------------------------------------------------------------- *
5  *                       Simbody(tm): SimTKcommon                             *
6  * -------------------------------------------------------------------------- *
7  * This is part of the SimTK biosimulation toolkit originating from           *
8  * Simbios, the NIH National Center for Physics-Based Simulation of           *
9  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
10  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
11  *                                                                            *
12  * Portions copyright (c) 2008-12 Stanford University and the Authors.        *
13  * Authors: Michael Sherman                                                   *
14  * Contributors:                                                              *
15  *                                                                            *
16  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
17  * not use this file except in compliance with the License. You may obtain a  *
18  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
19  *                                                                            *
20  * Unless required by applicable law or agreed to in writing, software        *
21  * distributed under the License is distributed on an "AS IS" BASIS,          *
22  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
23  * See the License for the specific language governing permissions and        *
24  * limitations under the License.                                             *
25  * -------------------------------------------------------------------------- */
26 
27 #include "SimTKcommon/Scalar.h"
28 #include "SimTKcommon/SmallMatrix.h"
29 
30 #include "MatrixHelperRep.h"
31 
32 #include <cstddef>
33 
34 namespace SimTK {
35 
36 
37 /*-------------------------------- VectorHelper --------------------------------
38 
39 This abstract class represents a 1d matrix, a.k.a. a Vector or a RowVector
40 (covector). However, its use is not limited to the SimTK::Vector and and
41 SimTK::RowVector classes; any skinny Matrix or skinny slice of a fatter
42 Matrix might use a VectorHelper since it provides faster access to memory
43 than a generic 2d helper would.
44 
45 Most operations don't care whether this is a column or a row,
46 however we have to know so that we can support matrix operations on the
47 vector when necessary. For example, getElt(i,j) still has to work (as long
48 as the appropriate one of i or j is 0), even though getElt(i) is more
49 efficient and direction-agnostic.
50 
51 The most common layout is that all elements are stored, either consecutively
52 in memory or with a regular stride. Vectors constructed from a larger pool
53 of stored data via indexing are also important and need to be implemented
54 efficiently.
55 
56 TODO:  However, there are several other important
57 layouts that arise most commonly from row and column selections performed
58 on non-full matrices, like triangular or symmetric matrices. Supporting such
59 selections allows simple (if inefficient) implementations of operations on
60 mixed types of matrices by breaking them into row and column operations.
61 Vectors selected in this way can be repetitions of the same element (often
62 zero), negations or conjugations of stored elements, and may sometimes have
63 a single "distinguished" element whose value is known (this occurs for example
64 when crossing a non-stored unit diagonal).
65 
66 TODO: Finally, we allow a Vector to be formed of a composition of smaller
67 Vectors. Then the whole vector can be accessed by element or more efficiently
68 by segments.
69 ------------------------------------------------------------------------------*/
70 template <class S>
71 class VectorHelper : public MatrixHelperRep<S> {
72     typedef VectorHelper<S>         This;
73     typedef MatrixHelperRep<S>      Base;
74     typedef typename CNT<S>::TNeg   SNeg;
75     typedef typename CNT<S>::THerm  SHerm;
76     typedef VectorHelper<SNeg>      ThisNeg;
77     typedef VectorHelper<SHerm>     ThisHerm;
78 public:
VectorHelper(int esz,int cppesz,bool isRow)79     VectorHelper(int esz, int cppesz, bool isRow)
80     :   Base(esz,cppesz),m_row(isRow)
81     {
82     }
83 
84 
85     // A deep copy of a Vector will always return another Vector, so we'll
86     // change the return type here.
87     virtual This* createDeepCopy_() const = 0;
88 
89     // Just changing the return type here.
90     virtual This* cloneHelper_() const = 0;
91 
preferRowOrder_()92     bool preferRowOrder_() const {return m_row;}
93 
94 
95     // Forward the two-index operations to the appropriate one-index operation.
96 
97     // One of the indices must be zero.
eltIsStored_(int i,int j)98     bool eltIsStored_(int i, int j)           const {return eltIsStored_(i+j);}
getElt_(int i,int j)99     const S* getElt_ (int i, int j)           const {return getElt_(i+j);}
updElt_(int i,int j)100     S*       updElt_ (int i, int j)                 {return updElt_(i+j);}
getAnyElt_(int i,int j,S * value)101     void getAnyElt_  (int i, int j, S* value) const {getAnyElt_(i+j, value);}
102 
103 
104     // These are mandatory for vectors.
105     virtual bool eltIsStored_(int i)           const = 0;
106     virtual const S* getElt_ (int i)           const = 0;
107     virtual S*       updElt_ (int i)                 = 0;
108     virtual void getAnyElt_  (int i, S* value) const = 0;
109 
110     // (Positional) transpose view is identical to this one except that we'll
111     // call it a row rather than a column or vice versa.
createTransposeView_()112     This* createTransposeView_() {
113         This* p = cloneHelper_();
114         p->m_data = this->m_data;
115         p->m_row = !m_row;
116         p->m_actual.updStorage().setOrder
117            (p->m_row ? MatrixStorage::RowOrder : MatrixStorage::ColumnOrder);
118         return p;
119     }
120 
121     // Not sure if this should ever be supported.
createRegularView_(const EltBlock &,const EltIndexer &)122     MatrixHelperRep<S>*  createRegularView_(const EltBlock&, const EltIndexer&) {
123         SimTK_ERRCHK_ALWAYS(!"not implemented", "VectorHelper::createRegularView_()", "not implemented");
124         return 0;
125     }
126 
127 
128 protected:
129     bool    m_row; // true if this should be a row when treated as a matrix
130 };
131 
132 //----------------------------- FullVectorHelper -------------------------------
133 /// All elements of the Vector are stored. The simplest form of this has the
134 /// data pointing to the Vector's 0th element with all the rest following
135 /// consecutively in memory. Derived classes add stride or indexing and
136 /// optimize for scalar elements.
137 //------------------------------------------------------------------------------
138 template <class S>
139 class FullVectorHelper : public VectorHelper<S> {
140     typedef FullVectorHelper<S>  This;
141     typedef VectorHelper<S>      Base;
142 public:
FullVectorHelper(int esz,int cppesz,bool isRow)143     FullVectorHelper(int esz, int cppesz, bool isRow)
144     :   Base(esz,cppesz,isRow) {}
145 
146     // This will always produce a 1-element "contiguous" column vector.
147     VectorHelper<S>* createDiagonalView_();
148 
149     // Source matches size and shape of this row or column.
copyInFromCompatibleSource_(const MatrixHelperRep<S> & source)150     void copyInFromCompatibleSource_(const MatrixHelperRep<S>& source) {
151         if (this->getEltSize() == 1) {
152             // The elements are scalars, so we can copy them directly.
153             if (this->nrow() == 1) // a row vector
154                 for (int j=0; j<this->ncol(); ++j)
155                     *this->updElt_(j) = *source.getElt(0,j);
156             else // a column vector
157                 for (int i=0; i<this->nrow(); ++i)
158                     *this->updElt_(i) = *source.getElt(i,0);
159         }
160         else {  // Here the elements are not scalars.
161             if (this->nrow() == 1) // a row vector
162                 for (int j=0; j<this->ncol(); ++j)
163                     this->copyElt(this->updElt_(j), source.getElt(0,j));
164             else // a column vector
165                 for (int i=0; i<this->nrow(); ++i)
166                     this->copyElt(this->updElt_(i), source.getElt(i,0));
167         }
168     }
169 };
170 
171 //-------------------------- ContiguousVectorHelper ----------------------------
172 /// All elements of the Vector are stored, and they are contiguous in memory.
173 /// This class handles general elements; a derived class handles the special
174 /// case of scalar elements by overriding some of the methods for speed.
175 //------------------------------------------------------------------------------
176 template <class S>
177 class ContiguousVectorHelper : public FullVectorHelper<S> {
178     typedef ContiguousVectorHelper<S>   This;
179     typedef FullVectorHelper<S>         Base;
180 public:
181     // Allocate our own space.
ContiguousVectorHelper(int esz,int cppesz,int n,bool isRow)182     ContiguousVectorHelper(int esz, int cppesz, int n, bool isRow)
183     :   Base(esz,cppesz,isRow)
184     {
185         this->m_owner     = true;
186         this->m_writable  = true;
187         this->allocateData(n);
188         this->m_actual.setStructure(MatrixStructure::Matrix1d);
189         this->m_actual.setStorage(
190             MatrixStorage(MatrixStorage::Vector, MatrixStorage::NoPlacement,
191                           isRow ? MatrixStorage::RowOrder : MatrixStorage::ColumnOrder,
192                           MatrixStorage::NoDiag));
193         this->m_actual.setActualSize(isRow?1:n, isRow?n:1); // apparent size; sets Outline
194     }
195 
196     // Use someone else's memory, which we assume to be the right size.
197     // We take care of stride elsewhere.
ContiguousVectorHelper(int esz,int cppesz,int n,bool isRow,const S * shared,bool canWrite)198     ContiguousVectorHelper(int esz, int cppesz, int n, bool isRow,
199                            const S* shared, bool canWrite)
200     :   Base(esz,cppesz,isRow)
201     {
202         this->m_owner     = false;
203         this->m_writable  = canWrite;
204         this->setData(const_cast<S*>(shared));
205         this->m_actual.setStructure(MatrixStructure::Matrix1d);
206         this->m_actual.setStorage(
207             MatrixStorage(MatrixStorage::Vector, MatrixStorage::NoPlacement,
208                           isRow ? MatrixStorage::RowOrder
209                                 : MatrixStorage::ColumnOrder,
210                           MatrixStorage::NoDiag));
211         // apparent size; sets Outline
212         this->m_actual.setActualSize(isRow?1:n, isRow?n:1);
213     }
214 
cloneHelper_()215     virtual This* cloneHelper_() const {return new This(*this);}
216 
217     // A block view of a full, contiguous row/column is either
218     // (1) a smaller full, contiguous row/column, or (2) a zero-width
219     // slice. In the latter case it may no longer be a row or column so we
220     // have to switch helper types to a Full mX0 or 0Xn matrix.
createBlockView_(const EltBlock & block)221     MatrixHelperRep<S>* createBlockView_(const EltBlock& block) {
222         const int m=block.nrow(), n=block.ncol();
223         if (m && n) { // normal case; same orientation but smaller
224             This* p = cloneHelper_();
225             p->m_data = updElt_(block.row0() + block.col0());
226             return p;
227         }
228         // Here we know at least one of m or n is zero.
229         if (m==1 || n==1) { // i.e., (1,0) or (0,1)
230             This* p = cloneHelper_(); // still 1d "contiguous"
231             p->m_data = 0;
232             p->m_row = (m==1); // regardless of what it was
233             return p;
234         }
235 
236         // Here one of m,n is zero and the other is > 1; not 1d any more.
237         RegularFullHelper<S>* p = 0;
238         if (this->getEltSize()==1)
239              p = new FullColOrderScalarHelper<S>(m,n,m,(S*)0,this->m_writable);
240         else p = new FullColOrderEltHelper<S>(this->getEltSize(),
241                                               this->getCppEltSize(),
242                                               m,n,m,(S*)0,this->m_writable);
243         // Called shared-data constructor so p is non-owner.
244         return p;
245     }
246 
247     // This creates an mx1 column vector.
createColumnView_(int j,int i,int m)248     This* createColumnView_(int j, int i, int m) {
249         This* p = cloneHelper_();
250         p->m_data = m > 0 ? updElt_(i+j) : 0;
251         p->m_row = false; // even if this was a row
252         p->m_actual.updStorage().setOrder(MatrixStorage::ColumnOrder);
253         return p;
254     }
255 
256     // This creates a 1xn row vector.
createRowView_(int i,int j,int n)257     This* createRowView_(int i, int j, int n) {
258         This* p = cloneHelper_();
259         p->m_data = n > 0 ? updElt_(i+j) : 0;
260         p->m_row = true; // even if this was a column
261         p->m_actual.updStorage().setOrder(MatrixStorage::RowOrder);
262         return p;
263     }
264 
265     // Override for strided or indexed data.
hasContiguousData_()266     virtual bool hasContiguousData_() const {return true;}
267     // Override for indexed data.
hasRegularData_()268     virtual bool hasRegularData_() const {return true;}
269 
getElt_(int i)270     const S* getElt_ (int i)           const {return this->m_data + i*this->m_eltSize;}
updElt_(int i)271     S*       updElt_ (int i)                 {return this->m_data + i*this->m_eltSize;}
eltIsStored_(int i)272     bool eltIsStored_(int i)           const {return true;}
273 
274     // Every element is stored so this just forwards to getElt(i).
getAnyElt_(int i,S * value)275     void getAnyElt_(int i, S* value) const
276     {   this->copyElt(value, getElt_(i)); }
277 
278     // OK for any size elements that are packed contiguously.
createDeepCopy_()279     This* createDeepCopy_() const {
280         This* p = cloneHelper_();
281         p->m_writable = true;
282         p->m_owner = true;
283         p->allocateData(this->nelt());
284         std::copy(this->m_data, this->m_data +
285                   this->length()*this->m_eltSize, p->m_data);
286         return p;
287     }
288 
289     // One of the lengths must be 1.
resize_(int m,int n)290     void resize_     (int m, int n)                 {resize_(m*n);}
resizeKeep_(int m,int n)291     void resizeKeep_ (int m, int n)                 {resizeKeep_(m*n);}
292 
293     // OK for any size elements.
resize_(int n)294     void resize_(int n) {
295         this->clearData();
296         this->allocateData(n);
297     }
298 
299     // OK for any size elements that are packed contiguously.
resizeKeep_(int n)300     void resizeKeep_(int n) {
301         S* const newData = this->allocateMemory(n);
302         const int nToCopy = std::min(n, this->length());
303         std::copy(this->m_data, this->m_data +
304                   nToCopy*this->m_eltSize, newData);
305         this->clearData();
306         this->setData(newData);
307     }
308 
copyInFromCompatibleSource_(const MatrixHelperRep<S> & source)309     void copyInFromCompatibleSource_(const MatrixHelperRep<S>& source) {
310         if (source.hasContiguousData() && this->nScalars())
311             std::copy(source.getElt(0,0), source.getElt(0,0) +
312                       this->nScalars(), this->m_data);
313         else
314             FullVectorHelper<S>::copyInFromCompatibleSource_(source);
315     }
316 };
317 
318 
319 //----------------------- ContiguousVectorScalarHelper -------------------------
320 /// All elements of the Vector are stored, and they are contiguous in memory,
321 /// and the elements are scalars. This inherits most functionality from the
322 /// contiguous general-element case.
323 //------------------------------------------------------------------------------
324 template <class S>
325 class ContiguousVectorScalarHelper : public ContiguousVectorHelper<S> {
326     typedef ContiguousVectorScalarHelper<S>     This;
327     typedef ContiguousVectorHelper<S>           Base;
328 public:
329     // Allocate our own space.
ContiguousVectorScalarHelper(int n,bool isRow)330     ContiguousVectorScalarHelper(int n, bool isRow) : Base(1,1,n,isRow) {}
331     // Use someone else's memory.
ContiguousVectorScalarHelper(int n,bool isRow,const S * shared,bool canWrite)332     ContiguousVectorScalarHelper(int n, bool isRow, const S* shared, bool canWrite)
333     :   Base(1,1,n,isRow,shared,canWrite) {}
334 
cloneHelper_()335     This* cloneHelper_() const {return new This(*this);}
336 
getElt_(int i)337     const S* getElt_ (int i) const {return this->m_data + i;}
updElt_(int i)338     S*       updElt_ (int i)       {return this->m_data + i;}
339 
340     // Every element is stored so this just forwards to getElt(i).
getAnyElt_(int i,S * value)341     void getAnyElt_(int i, S* value) const {*value = *getElt_(i);}
342 };
343 
344 
345 template <class S> inline VectorHelper<S>*
createDiagonalView_()346 FullVectorHelper<S>::createDiagonalView_() {
347     VectorHelper<S>* p = 0;
348     const int nDiags = std::min(this->length(), 1); // 0 or 1
349     S* data = nDiags ? this->updElt_(0) : 0;
350 
351     p = (this->m_eltSize==1)
352         ? new ContiguousVectorScalarHelper<S>(nDiags, false, data, false)
353         : new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize, nDiags,
354                                         false, data, false);
355     return p;
356 }
357 
358 
359 //---------------------------- StridedVectorHelper -----------------------------
360 /// This is a vector with regularly-spaced but non-contiguous elements. This
361 /// is only for views so does not include an implementation for resizing.
362 //------------------------------------------------------------------------------
363 template <class S>
364 class StridedVectorHelper : public FullVectorHelper<S> {
365     typedef StridedVectorHelper<S>  This;
366     typedef FullVectorHelper<S>     Base;
367 public:
368     /// Use someone else's memory, which we assume to be the right size. Note
369     /// that stride is given in elements, with stride==1 meaning the elements
370     /// are packed contiguously, in which case this is the wrong helper class to use.
StridedVectorHelper(int esz,int cppesz,int n,bool isRow,int strideInElements,const S * shared,bool canWrite)371     StridedVectorHelper(int esz, int cppesz, int n, bool isRow,
372          int strideInElements, const S* shared, bool canWrite)
373     :   Base(esz,cppesz,isRow), m_spacing((ptrdiff_t)strideInElements * esz)
374     {
375         SimTK_ASSERT1(strideInElements >= 2,
376             "StridedVectorHelper::ctor(): illegal stride %d", strideInElements);
377         this->m_owner     = false;
378         this->m_writable  = canWrite;
379         this->setData(const_cast<S*>(shared));
380         this->m_actual.setStructure(MatrixStructure::Matrix1d);
381         this->m_actual.setStorage(
382             MatrixStorage(MatrixStorage::Vector, MatrixStorage::NoPlacement,
383                           isRow ? MatrixStorage::RowOrder : MatrixStorage::ColumnOrder,
384                           MatrixStorage::NoDiag));
385         this->m_actual.setActualSize(isRow?1:n, isRow?n:1); // apparent size; sets Outline
386     }
387 
cloneHelper_()388     virtual This* cloneHelper_() const {return new This(*this);}
389 
390     // A block view of a strided vector is usually a smaller vector with
391     // identical stride. No stride needed if there are fewer than two
392     // elements, though. Also, this could be an mX0 or 0Xn slice which is
393     // no longer a Vector unless m==1 or n==1.
createBlockView_(const EltBlock & block)394      MatrixHelperRep<S>* createBlockView_(const EltBlock& block) {
395         const int m=block.nrow(), n=block.ncol();
396         if ((m==0 && n!=1) || (n==0 && m!=1)) {
397             // One or both dimensions is 0 and the other is not 1, so this
398             // is no longer a 1d object.
399             RegularFullHelper<S>* p = 0;
400             if (this->getEltSize()==1)
401                  p = new FullColOrderScalarHelper<S>(m,n,m,(S*)0,this->m_writable);
402             else p = new FullColOrderEltHelper<S>(this->getEltSize(),
403                                                   this->getCppEltSize(),
404                                                   m,n,m,(S*)0,this->m_writable);
405             // Called shared-data constructor so p is non-owner.
406             return p;
407         }
408 
409         // At least one of m,n is a 1. Could still be 1x0 or 0x1.
410 
411         VectorHelper<S>* p = 0;
412         const int start = block.row0() + block.col0(); // one of those is zero
413         const int length = m*n;  // one of those is one
414         S* data = length ? updElt_(start) : 0;
415 
416         if (length <= 1) {
417             p = (this->m_eltSize==1)
418                 ? new ContiguousVectorScalarHelper<S>(length, false, data, false)
419                 : new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize, length,
420                                                 false, data, false);
421             // called a shared-data constructor, so p is non-owner
422             return p;
423         }
424 
425         p = cloneHelper_();
426         p->setData(data);
427         return p;
428     }
429 
430     // Row and column view are like block view but without the possibility
431     // of a non-Vector result.
createColumnView_(int j,int i,int m)432     VectorHelper<S>* createColumnView_(int j, int i, int m) {
433         VectorHelper<S>* p = 0;
434         const int start = i+j; // one of those is zero
435         S* data = m ? updElt_(start) : 0;
436 
437         if (m <= 1) {
438             p = (this->m_eltSize==1)
439                 ? new ContiguousVectorScalarHelper<S>(m, false, data, false)
440                 : new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize, m,
441                                                 false, data, false);
442             return p;
443         }
444 
445         // length is > 1 so this must already be a column
446         assert(j==0);
447         p = cloneHelper_();
448         p->setData(data);
449         return p;
450     }
451 
createRowView_(int i,int j,int n)452     VectorHelper<S>* createRowView_(int i, int j, int n) {
453         VectorHelper<S>* p = 0;
454         const int start = i+j; // one of those is zero
455         S* data = n ? updElt_(start) : 0;
456 
457         if (n <= 1) {
458             p = (this->m_eltSize==1)
459                 ? new ContiguousVectorScalarHelper<S>(n, true, data, false)
460                 : new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize, n,
461                                                 true, data, false);
462             return p;
463         }
464 
465         // length is > 1 so this must already be a row
466         assert(i==0);
467         p = cloneHelper_();
468         p->setData(data);
469         return p;
470     }
471 
472 
hasContiguousData_()473     bool hasContiguousData_() const {return false;}
hasRegularData_()474     bool hasRegularData_()    const {return true;}
475 
eltIsStored_(int i)476     bool eltIsStored_(int i)           const {return true;}
getElt_(int i)477     const S* getElt_ (int i)           const {return this->m_data + i*m_spacing;}
updElt_(int i)478     S*       updElt_ (int i)                 {return this->m_data + i*m_spacing;}
479 
480     // Every element is stored so this just forwards to getElt(i).
getAnyElt_(int i,S * value)481     void getAnyElt_(int i, S* value) const
482     {   this->copyElt(value, getElt_(i)); }
483 
484     /// A deep copy of a strided vector produces a contiguous (stride==1)
485     /// vector containing the same number of elements.
createDeepCopy_()486     FullVectorHelper<S>* createDeepCopy_() const {
487         ContiguousVectorHelper<S>* p =
488             new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
489                                           this->length(), this->m_row);
490         for (int i=0; i < this->length(); ++i)
491             this->copyElt(p->updData() + i*this->m_eltSize, this->getData() + i*m_spacing);
492         return p;
493     }
494 
495 protected:
496     ptrdiff_t m_spacing; // m_eltsize*stride (spacing in scalars)
497 };
498 
499 
500 //------------------------- StridedVectorScalarHelper --------------------------
501 /// This is a vector with regularly-spaced but non-contiguous elements, where
502 /// the elements are scalars. Most functionality is inherited from the general-
503 /// element case but we specialize a few methods here for speed. This
504 /// is only for views so does not include an implementation for resizing.
505 //------------------------------------------------------------------------------
506 template <class S>
507 class StridedVectorScalarHelper : public StridedVectorHelper<S> {
508     typedef StridedVectorScalarHelper<S>    This;
509     typedef StridedVectorHelper<S>          Base;
510 public:
511     /// Use someone else's memory, which we assume to be the right size. Note
512     /// that stride is given in elements, with stride==1 meaning the elements
513     /// are packed contiguously, in which case this is the wrong helper class to use.
StridedVectorScalarHelper(int n,bool isRow,int stride,const S * shared,bool canWrite)514     StridedVectorScalarHelper(int n, bool isRow, int stride, const S* shared, bool canWrite)
515     :   Base(1,1,n,isRow,stride,shared,canWrite) {}
516 
cloneHelper_()517     This* cloneHelper_() const {return new This(*this);}
518 
519     // Every element is stored so this just forwards to getElt(i).
getAnyElt_(int i,S * value)520     void getAnyElt_(int i, S* value) const {*value = *this->getElt_(i);}
521 
522     /// A deep copy of a strided vector produces a contiguous (stride==1)
523     /// vector containing the same number of elements.
createDeepCopy_()524     FullVectorHelper<S>* createDeepCopy_() const {
525         ContiguousVectorScalarHelper<S>* p =
526             new ContiguousVectorScalarHelper<S>(this->length(), this->m_row);
527         const int nToCopy = this->length();
528         for (int i=0; i < nToCopy; ++i)
529             p->updData()[i] = this->getData()[i*this->m_spacing];
530         return p;
531     }
532 };
533 
534 //--------------------------- IndexedVectorHelper ------------------------------
535 /// All elements of the Vector are stored, but they are selected irregularly
536 /// from the available data. This is only for views so does not include an
537 /// implementation for resizing, and there is no constructor for data allocation.
538 /// This class handles general elements; a derived class handles the special
539 /// case of scalar elements by overriding some of the methods for speed.
540 /// TODO: we only allow 32 bit indices, although 64 bit indices
541 /// are possible (just need another helper class for "long long" indices).
542 //------------------------------------------------------------------------------
543 template <class S>
544 class IndexedVectorHelper : public FullVectorHelper<S> {
545     typedef IndexedVectorHelper<S>  This;
546     typedef FullVectorHelper<S>     Base;
547 public:
548     // Use someone else's memory, which we assume to be the right size. Here the
549     // indices must be in terms of elements. We'll store them internally in terms
550     // of scalars instead. We insist here that the indices are all nonnegative and
551     // in monotonically increasing order.
IndexedVectorHelper(int esz,int cppesz,int n,bool isRow,const int * eltIndices,const S * shared,bool canWrite)552     IndexedVectorHelper(int esz, int cppesz, int n, bool isRow,
553          const int* eltIndices, const S* shared, bool canWrite)
554     :   Base(esz,cppesz,isRow), m_scalarIndices(0)
555     {
556         this->m_owner     = false;
557         this->m_writable  = canWrite;
558         this->setData(const_cast<S*>(shared));
559         this->m_actual.setStructure(MatrixStructure::Matrix1d);
560         this->m_actual.setStorage(
561             MatrixStorage(MatrixStorage::Vector, MatrixStorage::NoPlacement,
562                           isRow ? MatrixStorage::RowOrder : MatrixStorage::ColumnOrder,
563                           MatrixStorage::NoDiag));
564         this->m_actual.setActualSize(isRow?1:n, isRow?n:1); // apparent size; sets Outline
565 
566         if (n) {
567             m_scalarIndices = new int[n];
568             for (int i=0; i<n; ++i) {
569                 SimTK_ERRCHK(i==0 || eltIndices[i]>eltIndices[i-1], "IndexedVectorHelper::ctor()",
570                     "Indices must be in monotonically ascending order.");
571                 m_scalarIndices[i] = this->m_eltSize*eltIndices[i];
572             }
573         }
574     }
575 
576     // Copy constructor must copy indices.
IndexedVectorHelper(const This & src)577     IndexedVectorHelper(const This& src) : Base(src), m_scalarIndices(0) {
578         if (src.length()) {
579             m_scalarIndices = new int[src.length()];
580             std::copy(src.m_scalarIndices, src.m_scalarIndices +
581                       src.length(), m_scalarIndices);
582         }
583     }
584 
585 
586     // (Virtual) destructor must delete indices.
~IndexedVectorHelper()587     ~IndexedVectorHelper() {delete[] m_scalarIndices;}
588 
589     // Note that cloning the helper also copies the indices.
cloneHelper_()590     virtual This* cloneHelper_() const {return new This(*this);}
591 
592     // A block view of an indexed vector is a shorter indexed vector.
593     // Also, this could be an mX0 or 0Xn slice which is
594     // no longer a Vector unless m==1 or n==1.
createBlockView_(const EltBlock & block)595      MatrixHelperRep<S>* createBlockView_(const EltBlock& block) {
596         const int m=block.nrow(), n=block.ncol();
597         if ((m==0 && n!=1) || (n==0 && m!=1)) {
598             // One or both dimensions is 0 and the other is not 1, so this
599             // is no longer a 1d object.
600             RegularFullHelper<S>* p = 0;
601             if (this->getEltSize()==1)
602                  p = new FullColOrderScalarHelper<S>(m,n,m,(S*)0,this->m_writable);
603             else p = new FullColOrderEltHelper<S>(this->getEltSize(),
604                                                   this->getCppEltSize(),
605                                                   m,n,m,(S*)0,this->m_writable);
606             // Called a shared-data constructor so p is non-owner.
607             return p;
608         }
609 
610         // At least one of the dimensions is a 1. Could still be 1x0 or 0x1.
611 
612         const int start = block.row0() + block.col0(); // one of these is zero
613         const int length = block.nrow()*block.ncol(); // one of these is 1
614 
615         if (length <= 1) {
616             // No need for indices; this is contiguous now.
617             S* data = length ? updElt_(start) : 0;
618             ContiguousVectorHelper<S>* p = (this->m_eltSize==1)
619                 ? new ContiguousVectorScalarHelper<S>
620                         (length, false, data, false)
621                 : new ContiguousVectorHelper<S>
622                         (this->m_eltSize, this->m_cppEltSize, length,
623                          false, data, false);
624             // Called a shared-data constructor so p is non-owner.
625             return p;
626         }
627 
628         // Still an indexed vector.
629 
630         This* p = new This(*this, true); // don't copy the indices
631         p->m_data = this->m_data;
632         p->m_scalarIndices = new int[length];
633         std::copy(m_scalarIndices+start, m_scalarIndices+start +
634                   length, p->m_scalarIndices);
635         return p;
636     }
637 
createColumnView_(int j,int i,int m)638     VectorHelper<S>* createColumnView_(int j, int i, int m) {
639         if (m <= 1) {
640             S* data = m ? updElt_(i+j) : 0; // one of i or j is 0
641             VectorHelper<S>* p = (this->m_eltSize==1)
642                 ? (VectorHelper<S>*)new ContiguousVectorScalarHelper<S>(m, false, data, false)
643                 : (VectorHelper<S>*)new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
644                                                                   m, false, data, false);
645             return p;
646         }
647 
648         // This must already be a column or we couldn't make a >1 element column here.
649         assert(j==0);
650         This* p = new This(*this, true); // don't copy the indices
651         p->setData(this->m_data); // leaving the indices the same, so data starts at 0
652         p->m_scalarIndices = new int[m];
653         std::copy(m_scalarIndices+i, m_scalarIndices+i+m, p->m_scalarIndices);
654         return p;
655     }
656 
657 
createRowView_(int i,int j,int n)658     VectorHelper<S>* createRowView_(int i, int j, int n) {
659         if (n<= 1) {
660             S* data = n ? updElt_(i+j) : 0; // one of i or j is 0
661             VectorHelper<S>* p = (this->m_eltSize==1)
662                 ? (VectorHelper<S>*)new ContiguousVectorScalarHelper<S>(n, true, data, false)
663                 : (VectorHelper<S>*)new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
664                                                                   n, true, data, false);
665             return p;
666         }
667 
668         // This must already be a row or we couldn't make a >1 element row here.
669         assert(i==0);
670         This* p = new This(*this, true); // don't copy the indices
671         p->setData(this->m_data); // leaving the indices the same, so data starts at 0
672         p->m_scalarIndices = new int[n];
673         std::copy(m_scalarIndices+j, m_scalarIndices+j+n, p->m_scalarIndices);
674         return p;
675     }
676 
hasContiguousData_()677     bool hasContiguousData_() const {return false;}
hasRegularData_()678     bool hasRegularData_()    const {return false;}
679 
getElt_(int i)680     const S* getElt_ (int i)           const {return this->m_data + m_scalarIndices[i];}
updElt_(int i)681     S*       updElt_ (int i)                 {return this->m_data + m_scalarIndices[i];}
eltIsStored_(int i)682     bool eltIsStored_(int i)           const {return true;}
683 
684     // Every element is stored so this just forwards to getElt(i).
getAnyElt_(int i,S * value)685     void getAnyElt_(int i, S* value) const
686     {   this->copyElt(value, getElt_(i)); }
687 
688     // A deep copy of an indexed vector produces a contiguous, non-indexed vector.
createDeepCopy_()689     ContiguousVectorHelper<S>* createDeepCopy_() const {
690         ContiguousVectorHelper<S>* p =
691             new ContiguousVectorHelper<S>(this->m_eltSize, this->m_cppEltSize,
692                                           this->length(), this->m_row);
693         for (int i=0; i<this->length(); ++i)
694             this->copyElt(p->updElt_(i), getElt_(i));
695         return p;
696     }
697 
698 protected:
699     int*  m_scalarIndices;
700 
701 private:
702     // This is like a copy constructor, but it does not copy the indices. The second
703     // parameter is a dummy.
IndexedVectorHelper(const This & src,bool)704     IndexedVectorHelper(const This& src, bool) : Base(src), m_scalarIndices(0) {}
705 };
706 
707 
708 
709 } // namespace SimTK
710 
711 
712 #endif // SimTK_SimTKCOMMON_MATRIX_HELPER_REP_VECTOR_H_
713