Mercurial > hg > octave-nkf
annotate liboctave/Sparse.h @ 11842:0b9c56b6bf0e release-3-0-x
partially sync Matrix::expm and ComplexMatrix::expm with development repo
author | Jaroslav Hajek <highegg@gmail.com> |
---|---|
date | Fri, 19 Sep 2008 11:29:51 +0200 |
parents | 7cc783e52ddb |
children |
rev | line source |
---|---|
5164 | 1 // Template sparse classes |
2 /* | |
3 | |
11758 | 4 Copyright (C) 2004, 2005, 2006, 2007, 2008 David Bateman |
7016 | 5 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 Andy Adler |
6 | |
7 This file is part of Octave. | |
5164 | 8 |
9 Octave is free software; you can redistribute it and/or modify it | |
10 under the terms of the GNU General Public License as published by the | |
7016 | 11 Free Software Foundation; either version 3 of the License, or (at your |
12 option) any later version. | |
5164 | 13 |
14 Octave is distributed in the hope that it will be useful, but WITHOUT | |
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
17 for more details. | |
18 | |
19 You should have received a copy of the GNU General Public License | |
7016 | 20 along with Octave; see the file COPYING. If not, see |
21 <http://www.gnu.org/licenses/>. | |
5164 | 22 |
23 */ | |
24 | |
25 #if !defined (octave_Sparse_h) | |
26 #define octave_Sparse_h 1 | |
27 | |
28 #include <cassert> | |
29 #include <cstddef> | |
30 | |
31 #include <iostream> | |
32 | |
33 #include "Array.h" | |
34 #include "Array2.h" | |
35 #include "dim-vector.h" | |
36 #include "lo-utils.h" | |
37 | |
38 class idx_vector; | |
39 | |
40 // Two dimensional sparse class. Handles the reference counting for | |
41 // all the derived classes. | |
42 | |
43 template <class T> | |
44 class | |
45 Sparse | |
46 { | |
47 protected: | |
48 //-------------------------------------------------------------------- | |
49 // The real representation of all Sparse arrays. | |
50 //-------------------------------------------------------------------- | |
51 | |
6108 | 52 class OCTAVE_API SparseRep |
5164 | 53 { |
54 public: | |
55 | |
56 T *d; | |
5275 | 57 octave_idx_type *r; |
58 octave_idx_type *c; | |
5604 | 59 octave_idx_type nzmx; |
5275 | 60 octave_idx_type nrows; |
61 octave_idx_type ncols; | |
5164 | 62 int count; |
63 | |
5604 | 64 SparseRep (void) : d (0), r (0), c (new octave_idx_type [1]), nzmx (0), nrows (0), |
5164 | 65 ncols (0), count (1) { c[0] = 0; } |
66 | |
5604 | 67 SparseRep (octave_idx_type n) : d (0), r (0), c (new octave_idx_type [n+1]), nzmx (0), nrows (n), |
5164 | 68 ncols (n), count (1) |
69 { | |
5275 | 70 for (octave_idx_type i = 0; i < n + 1; i++) |
5164 | 71 c[i] = 0; |
72 } | |
73 | |
5604 | 74 SparseRep (octave_idx_type nr, octave_idx_type nc) : d (0), r (0), c (new octave_idx_type [nc+1]), nzmx (0), |
5164 | 75 nrows (nr), ncols (nc), count (1) |
76 { | |
5275 | 77 for (octave_idx_type i = 0; i < nc + 1; i++) |
5164 | 78 c[i] = 0; |
79 } | |
80 | |
5275 | 81 SparseRep (octave_idx_type nr, octave_idx_type nc, octave_idx_type nz) : d (new T [nz]), |
5604 | 82 r (new octave_idx_type [nz]), c (new octave_idx_type [nc+1]), nzmx (nz), nrows (nr), |
5164 | 83 ncols (nc), count (1) |
84 { | |
5275 | 85 for (octave_idx_type i = 0; i < nc + 1; i++) |
5164 | 86 c[i] = 0; |
87 } | |
88 | |
89 SparseRep (const SparseRep& a) | |
5604 | 90 : d (new T [a.nzmx]), r (new octave_idx_type [a.nzmx]), c (new octave_idx_type [a.ncols + 1]), |
91 nzmx (a.nzmx), nrows (a.nrows), ncols (a.ncols), count (1) | |
5164 | 92 { |
5604 | 93 for (octave_idx_type i = 0; i < nzmx; i++) |
5164 | 94 { |
95 d[i] = a.d[i]; | |
96 r[i] = a.r[i]; | |
97 } | |
5275 | 98 for (octave_idx_type i = 0; i < ncols + 1; i++) |
5164 | 99 c[i] = a.c[i]; |
100 } | |
101 | |
102 ~SparseRep (void) { delete [] d; delete [] r; delete [] c; } | |
103 | |
5604 | 104 octave_idx_type length (void) const { return nzmx; } |
5164 | 105 |
5604 | 106 octave_idx_type nnz (void) const { return c [ncols]; } |
5164 | 107 |
5275 | 108 T& elem (octave_idx_type _r, octave_idx_type _c); |
5164 | 109 |
5275 | 110 T celem (octave_idx_type _r, octave_idx_type _c) const; |
5164 | 111 |
5275 | 112 T& data (octave_idx_type i) { return d[i]; } |
5164 | 113 |
5275 | 114 T cdata (octave_idx_type i) const { return d[i]; } |
5164 | 115 |
5275 | 116 octave_idx_type& ridx (octave_idx_type i) { return r[i]; } |
5164 | 117 |
5275 | 118 octave_idx_type cridx (octave_idx_type i) const { return r[i]; } |
5164 | 119 |
5275 | 120 octave_idx_type& cidx (octave_idx_type i) { return c[i]; } |
5164 | 121 |
5275 | 122 octave_idx_type ccidx (octave_idx_type i) const { return c[i]; } |
5164 | 123 |
124 void maybe_compress (bool remove_zeros); | |
125 | |
5275 | 126 void change_length (octave_idx_type nz); |
5164 | 127 |
128 private: | |
129 | |
130 // No assignment! | |
131 | |
132 SparseRep& operator = (const SparseRep& a); | |
133 }; | |
134 | |
135 //-------------------------------------------------------------------- | |
136 | |
137 void make_unique (void) | |
138 { | |
139 if (rep->count > 1) | |
140 { | |
141 --rep->count; | |
142 rep = new SparseRep (*rep); | |
143 } | |
144 } | |
145 | |
146 public: | |
147 | |
148 // !!! WARNING !!! -- these should be protected, not public. You | |
149 // should not access these data members directly! | |
150 | |
151 typename Sparse<T>::SparseRep *rep; | |
152 | |
153 dim_vector dimensions; | |
154 | |
155 protected: | |
156 idx_vector *idx; | |
5275 | 157 octave_idx_type idx_count; |
5164 | 158 |
159 private: | |
160 | |
161 typename Sparse<T>::SparseRep *nil_rep (void) const | |
162 { | |
163 static typename Sparse<T>::SparseRep *nr | |
164 = new typename Sparse<T>::SparseRep (); | |
165 | |
166 nr->count++; | |
167 | |
168 return nr; | |
169 } | |
170 | |
171 public: | |
172 | |
173 Sparse (void) | |
174 : rep (nil_rep ()), dimensions (dim_vector(0,0)), | |
175 idx (0), idx_count (0) { } | |
176 | |
5275 | 177 explicit Sparse (octave_idx_type n) |
5164 | 178 : rep (new typename Sparse<T>::SparseRep (n)), |
179 dimensions (dim_vector (n, n)), idx (0), idx_count (0) { } | |
180 | |
5275 | 181 explicit Sparse (octave_idx_type nr, octave_idx_type nc) |
5164 | 182 : rep (new typename Sparse<T>::SparseRep (nr, nc)), |
183 dimensions (dim_vector (nr, nc)), idx (0), idx_count (0) { } | |
184 | |
5275 | 185 explicit Sparse (octave_idx_type nr, octave_idx_type nc, T val); |
5164 | 186 |
5275 | 187 Sparse (const dim_vector& dv, octave_idx_type nz) |
5164 | 188 : rep (new typename Sparse<T>::SparseRep (dv(0), dv(1), nz)), |
189 dimensions (dv), idx (0), idx_count (0) { } | |
190 | |
5275 | 191 Sparse (octave_idx_type nr, octave_idx_type nc, octave_idx_type nz) |
5164 | 192 : rep (new typename Sparse<T>::SparseRep (nr, nc, nz)), |
193 dimensions (dim_vector (nr, nc)), idx (0), idx_count (0) { } | |
194 | |
195 // Type conversion case. | |
196 template <class U> Sparse (const Sparse<U>& a); | |
197 | |
198 // No type conversion case. | |
199 Sparse (const Sparse<T>& a) | |
200 : rep (a.rep), dimensions (a.dimensions), idx (0), idx_count (0) | |
201 { | |
202 rep->count++; | |
203 } | |
204 | |
205 public: | |
206 | |
207 Sparse (const dim_vector& dv); | |
208 | |
209 Sparse (const Sparse<T>& a, const dim_vector& dv); | |
210 | |
5275 | 211 Sparse (const Array<T>& a, const Array<octave_idx_type>& r, const Array<octave_idx_type>& c, |
212 octave_idx_type nr, octave_idx_type nc, bool sum_terms); | |
5164 | 213 |
214 Sparse (const Array<T>& a, const Array<double>& r, const Array<double>& c, | |
5275 | 215 octave_idx_type nr, octave_idx_type nc, bool sum_terms); |
5164 | 216 |
217 // Sparsify a normal matrix | |
218 Sparse (const Array2<T>& a); | |
219 Sparse (const Array<T>& a); | |
220 | |
221 virtual ~Sparse (void); | |
222 | |
11755
ccab9d3d1d21
Delete idx in Sparse<T> and Array<T> operator =
David Bateman <dbateman@free.fr>
parents:
7017
diff
changeset
|
223 Sparse<T>& operator = (const Sparse<T>& a); |
5164 | 224 |
5604 | 225 // Note that nzmax and capacity are the amount of storage for |
226 // non-zero elements, while nnz is the actual number of non-zero | |
227 // terms. | |
228 octave_idx_type nzmax (void) const { return rep->length (); } | |
229 octave_idx_type capacity (void) const { return nzmax (); } | |
230 octave_idx_type nnz (void) const { return rep->nnz (); } | |
5164 | 231 |
232 // Paranoid number of elements test for case of dims = (-1,-1) | |
5275 | 233 octave_idx_type numel (void) const |
5164 | 234 { |
235 if (dim1() < 0 || dim2() < 0) | |
236 return 0; | |
237 else | |
238 return dimensions.numel (); | |
239 } | |
240 | |
5275 | 241 octave_idx_type nelem (void) const { return capacity (); } |
242 octave_idx_type length (void) const { return numel (); } | |
5164 | 243 |
5275 | 244 octave_idx_type dim1 (void) const { return dimensions(0); } |
245 octave_idx_type dim2 (void) const { return dimensions(1); } | |
5164 | 246 |
5275 | 247 octave_idx_type rows (void) const { return dim1 (); } |
248 octave_idx_type cols (void) const { return dim2 (); } | |
249 octave_idx_type columns (void) const { return dim2 (); } | |
5164 | 250 |
5275 | 251 octave_idx_type get_row_index (octave_idx_type k) { return ridx (k); } |
252 octave_idx_type get_col_index (octave_idx_type k) | |
5164 | 253 { |
5275 | 254 octave_idx_type ret = 0; |
5164 | 255 while (cidx(ret+1) < k) |
256 ret++; | |
257 return ret; | |
258 } | |
5275 | 259 size_t byte_size (void) const { return (cols () + 1) * sizeof (octave_idx_type) + |
260 capacity () * (sizeof (T) + sizeof (octave_idx_type)); } | |
5164 | 261 |
262 dim_vector dims (void) const { return dimensions; } | |
263 | |
264 Sparse<T> squeeze (void) const { return *this; } | |
265 | |
5275 | 266 octave_idx_type compute_index (const Array<octave_idx_type>& ra_idx) const; |
5164 | 267 |
5275 | 268 T range_error (const char *fcn, octave_idx_type n) const; |
269 T& range_error (const char *fcn, octave_idx_type n); | |
5164 | 270 |
5275 | 271 T range_error (const char *fcn, octave_idx_type i, octave_idx_type j) const; |
272 T& range_error (const char *fcn, octave_idx_type i, octave_idx_type j); | |
5164 | 273 |
5275 | 274 T range_error (const char *fcn, const Array<octave_idx_type>& ra_idx) const; |
275 T& range_error (const char *fcn, const Array<octave_idx_type>& ra_idx); | |
5164 | 276 |
277 // No checking, even for multiple references, ever. | |
278 | |
5275 | 279 T& xelem (octave_idx_type n) |
5164 | 280 { |
5275 | 281 octave_idx_type i = n % rows (), j = n / rows(); |
5164 | 282 return xelem (i, j); |
283 } | |
284 | |
5275 | 285 T xelem (octave_idx_type n) const |
5164 | 286 { |
5275 | 287 octave_idx_type i = n % rows (), j = n / rows(); |
5164 | 288 return xelem (i, j); |
289 } | |
290 | |
5275 | 291 T& xelem (octave_idx_type i, octave_idx_type j) { return rep->elem (i, j); } |
292 T xelem (octave_idx_type i, octave_idx_type j) const { return rep->celem (i, j); } | |
5164 | 293 |
5275 | 294 T& xelem (const Array<octave_idx_type>& ra_idx) |
5164 | 295 { return xelem (compute_index (ra_idx)); } |
296 | |
5275 | 297 T xelem (const Array<octave_idx_type>& ra_idx) const |
5164 | 298 { return xelem (compute_index (ra_idx)); } |
299 | |
5775 | 300 // FIXME -- would be nice to fix this so that we don't |
5164 | 301 // unnecessarily force a copy, but that is not so easy, and I see no |
302 // clean way to do it. | |
303 | |
5275 | 304 T& checkelem (octave_idx_type n) |
5164 | 305 { |
306 if (n < 0 || n >= numel ()) | |
307 return range_error ("T& Sparse<T>::checkelem", n); | |
308 else | |
309 { | |
310 make_unique (); | |
311 return xelem (n); | |
312 } | |
313 } | |
314 | |
5275 | 315 T& checkelem (octave_idx_type i, octave_idx_type j) |
5164 | 316 { |
317 if (i < 0 || j < 0 || i >= dim1 () || j >= dim2 ()) | |
318 return range_error ("T& Sparse<T>::checkelem", i, j); | |
319 else | |
320 { | |
321 make_unique (); | |
322 return xelem (i, j); | |
323 } | |
324 } | |
325 | |
5275 | 326 T& checkelem (const Array<octave_idx_type>& ra_idx) |
5164 | 327 { |
5275 | 328 octave_idx_type i = compute_index (ra_idx); |
5164 | 329 |
330 if (i < 0) | |
331 return range_error ("T& Sparse<T>::checkelem", ra_idx); | |
332 else | |
333 return elem (i); | |
334 } | |
335 | |
5275 | 336 T& elem (octave_idx_type n) |
5164 | 337 { |
338 make_unique (); | |
339 return xelem (n); | |
340 } | |
341 | |
5275 | 342 T& elem (octave_idx_type i, octave_idx_type j) |
5164 | 343 { |
344 make_unique (); | |
345 return xelem (i, j); | |
346 } | |
347 | |
5275 | 348 T& elem (const Array<octave_idx_type>& ra_idx) |
5164 | 349 { return Sparse<T>::elem (compute_index (ra_idx)); } |
350 | |
351 #if defined (BOUNDS_CHECKING) | |
5275 | 352 T& operator () (octave_idx_type n) { return checkelem (n); } |
353 T& operator () (octave_idx_type i, octave_idx_type j) { return checkelem (i, j); } | |
354 T& operator () (const Array<octave_idx_type>& ra_idx) { return checkelem (ra_idx); } | |
5164 | 355 #else |
5275 | 356 T& operator () (octave_idx_type n) { return elem (n); } |
357 T& operator () (octave_idx_type i, octave_idx_type j) { return elem (i, j); } | |
358 T& operator () (const Array<octave_idx_type>& ra_idx) { return elem (ra_idx); } | |
5164 | 359 #endif |
360 | |
5275 | 361 T checkelem (octave_idx_type n) const |
5164 | 362 { |
363 if (n < 0 || n >= numel ()) | |
364 return range_error ("T Sparse<T>::checkelem", n); | |
365 else | |
366 return xelem (n); | |
367 } | |
368 | |
5275 | 369 T checkelem (octave_idx_type i, octave_idx_type j) const |
5164 | 370 { |
371 if (i < 0 || j < 0 || i >= dim1 () || j >= dim2 ()) | |
372 return range_error ("T Sparse<T>::checkelem", i, j); | |
373 else | |
374 return xelem (i, j); | |
375 } | |
376 | |
5275 | 377 T checkelem (const Array<octave_idx_type>& ra_idx) const |
5164 | 378 { |
5275 | 379 octave_idx_type i = compute_index (ra_idx); |
5164 | 380 |
381 if (i < 0) | |
382 return range_error ("T Sparse<T>::checkelem", ra_idx); | |
383 else | |
384 return Sparse<T>::elem (i); | |
385 } | |
386 | |
5275 | 387 T elem (octave_idx_type n) const { return xelem (n); } |
5164 | 388 |
5275 | 389 T elem (octave_idx_type i, octave_idx_type j) const { return xelem (i, j); } |
5164 | 390 |
5275 | 391 T elem (const Array<octave_idx_type>& ra_idx) const |
5164 | 392 { return Sparse<T>::elem (compute_index (ra_idx)); } |
393 | |
394 #if defined (BOUNDS_CHECKING) | |
5275 | 395 T operator () (octave_idx_type n) const { return checkelem (n); } |
396 T operator () (octave_idx_type i, octave_idx_type j) const { return checkelem (i, j); } | |
397 T operator () (const Array<octave_idx_type>& ra_idx) const { return checkelem (ra_idx); } | |
5164 | 398 #else |
5275 | 399 T operator () (octave_idx_type n) const { return elem (n); } |
400 T operator () (octave_idx_type i, octave_idx_type j) const { return elem (i, j); } | |
401 T operator () (const Array<octave_idx_type>& ra_idx) const { return elem (ra_idx); } | |
5164 | 402 #endif |
403 | |
404 Sparse<T> maybe_compress (bool remove_zeros = false) | |
405 { rep->maybe_compress (remove_zeros); return (*this); } | |
406 | |
407 Sparse<T> reshape (const dim_vector& new_dims) const; | |
408 | |
409 // !!! WARNING !!! -- the following resize_no_fill functions are | |
410 // public because template friends don't work properly with versions | |
411 // of gcc earlier than 3.3. You should use these functions only in | |
412 // classes that are derived from Sparse<T>. | |
413 | |
414 // protected: | |
415 | |
5275 | 416 void resize_no_fill (octave_idx_type r, octave_idx_type c); |
5164 | 417 |
418 void resize_no_fill (const dim_vector& dv); | |
419 | |
420 public: | |
5275 | 421 Sparse<T> permute (const Array<octave_idx_type>& vec, bool inv = false) const; |
5164 | 422 |
5275 | 423 Sparse<T> ipermute (const Array<octave_idx_type>& vec) const |
5164 | 424 { return permute (vec, true); } |
425 | |
5275 | 426 void resize (octave_idx_type r, octave_idx_type c) { resize_no_fill (r, c); } |
5164 | 427 |
428 void resize (const dim_vector& dv) { resize_no_fill (dv); } | |
429 | |
5275 | 430 void change_capacity (octave_idx_type nz) { rep->change_length (nz); } |
5164 | 431 |
5275 | 432 Sparse<T>& insert (const Sparse<T>& a, octave_idx_type r, octave_idx_type c); |
433 Sparse<T>& insert (const Sparse<T>& a, const Array<octave_idx_type>& idx); | |
5164 | 434 |
435 bool is_square (void) const { return (dim1 () == dim2 ()); } | |
436 | |
437 bool is_empty (void) const { return (rows () < 1 && cols () < 1); } | |
438 | |
439 Sparse<T> transpose (void) const; | |
440 | |
441 T* data (void) { make_unique (); return rep->d; } | |
5275 | 442 T& data (octave_idx_type i) { make_unique (); return rep->data (i); } |
5164 | 443 T* xdata (void) { return rep->d; } |
5275 | 444 T& xdata (octave_idx_type i) { return rep->data (i); } |
5164 | 445 |
5275 | 446 T data (octave_idx_type i) const { return rep->data (i); } |
5900 | 447 // FIXME -- shouldn't this be returning const T*? |
5164 | 448 T* data (void) const { return rep->d; } |
449 | |
5275 | 450 octave_idx_type* ridx (void) { make_unique (); return rep->r; } |
451 octave_idx_type& ridx (octave_idx_type i) { make_unique (); return rep->ridx (i); } | |
452 octave_idx_type* xridx (void) { return rep->r; } | |
453 octave_idx_type& xridx (octave_idx_type i) { return rep->ridx (i); } | |
5164 | 454 |
5275 | 455 octave_idx_type ridx (octave_idx_type i) const { return rep->cridx (i); } |
5900 | 456 // FIXME -- shouldn't this be returning const octave_idx_type*? |
5275 | 457 octave_idx_type* ridx (void) const { return rep->r; } |
5164 | 458 |
5275 | 459 octave_idx_type* cidx (void) { make_unique (); return rep->c; } |
460 octave_idx_type& cidx (octave_idx_type i) { make_unique (); return rep->cidx (i); } | |
461 octave_idx_type* xcidx (void) { return rep->c; } | |
462 octave_idx_type& xcidx (octave_idx_type i) { return rep->cidx (i); } | |
5164 | 463 |
5275 | 464 octave_idx_type cidx (octave_idx_type i) const { return rep->ccidx (i); } |
5900 | 465 // FIXME -- shouldn't this be returning const octave_idx_type*? |
5275 | 466 octave_idx_type* cidx (void) const { return rep->c; } |
5164 | 467 |
5275 | 468 octave_idx_type ndims (void) const { return dimensions.length (); } |
5164 | 469 |
470 void clear_index (void); | |
471 | |
472 void set_index (const idx_vector& i); | |
473 | |
5275 | 474 octave_idx_type index_count (void) const { return idx_count; } |
5164 | 475 |
476 idx_vector *get_idx (void) const { return idx; } | |
477 | |
478 void maybe_delete_elements (idx_vector& i); | |
479 | |
480 void maybe_delete_elements (idx_vector& i, idx_vector& j); | |
481 | |
482 void maybe_delete_elements (Array<idx_vector>& ra_idx); | |
483 | |
484 Sparse<T> value (void); | |
485 | |
486 Sparse<T> index (idx_vector& i, int resize_ok = 0) const; | |
487 | |
488 Sparse<T> index (idx_vector& i, idx_vector& j, int resize_ok = 0) const; | |
489 | |
490 Sparse<T> index (Array<idx_vector>& ra_idx, int resize_ok = 0) const; | |
491 | |
492 void print_info (std::ostream& os, const std::string& prefix) const; | |
493 | |
5900 | 494 // Unsafe. These functions exist to support the MEX interface. |
495 // You should not use them anywhere else. | |
496 void *mex_get_data (void) const { return const_cast<T *> (data ()); } | |
497 | |
498 octave_idx_type *mex_get_ir (void) const { return const_cast<octave_idx_type *> (ridx ()); } | |
499 | |
500 octave_idx_type *mex_get_jc (void) const { return const_cast<octave_idx_type *> (cidx ()); } | |
5164 | 501 }; |
502 | |
503 // NOTE: these functions should be friends of the Sparse<T> class and | |
504 // Sparse<T>::dimensions should be protected, not public, but we can't | |
505 // do that because of bugs in gcc prior to 3.3. | |
506 | |
507 template <class LT, class RT> | |
508 /* friend */ int | |
509 assign (Sparse<LT>& lhs, const Sparse<RT>& rhs); | |
510 | |
511 template <class LT, class RT> | |
512 /* friend */ int | |
513 assign1 (Sparse<LT>& lhs, const Sparse<RT>& rhs); | |
514 | |
6708 | 515 #define INSTANTIATE_SPARSE_ASSIGN(LT, RT, API) \ |
516 template API int assign (Sparse<LT>&, const Sparse<RT>&); \ | |
517 template API int assign1 (Sparse<LT>&, const Sparse<RT>&); | |
5164 | 518 |
6708 | 519 #define INSTANTIATE_SPARSE(T, API) \ |
520 template class API Sparse<T>; | |
5164 | 521 |
6708 | 522 #define INSTANTIATE_SPARSE_AND_ASSIGN(T, API) \ |
523 INSTANTIATE_SPARSE (T, API); \ | |
524 INSTANTIATE_SPARSE_ASSIGN (T, T, API) | |
5164 | 525 |
526 #endif | |
527 | |
528 /* | |
529 ;;; Local Variables: *** | |
530 ;;; mode: C++ *** | |
531 ;;; End: *** | |
532 */ |