5164
|
1 // Template sparse classes |
|
2 /* |
|
3 |
|
4 Copyright (C) 2004 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 |
|
223 Sparse<T>& operator = (const Sparse<T>& a) |
|
224 { |
|
225 if (this != &a) |
|
226 { |
|
227 if (--rep->count <= 0) |
|
228 delete rep; |
|
229 |
|
230 rep = a.rep; |
|
231 rep->count++; |
|
232 |
|
233 dimensions = a.dimensions; |
|
234 } |
|
235 |
|
236 idx_count = 0; |
|
237 idx = 0; |
|
238 |
|
239 return *this; |
|
240 } |
|
241 |
5604
|
242 // Note that nzmax and capacity are the amount of storage for |
|
243 // non-zero elements, while nnz is the actual number of non-zero |
|
244 // terms. |
|
245 octave_idx_type nzmax (void) const { return rep->length (); } |
|
246 octave_idx_type capacity (void) const { return nzmax (); } |
|
247 octave_idx_type nnz (void) const { return rep->nnz (); } |
5164
|
248 |
|
249 // Paranoid number of elements test for case of dims = (-1,-1) |
5275
|
250 octave_idx_type numel (void) const |
5164
|
251 { |
|
252 if (dim1() < 0 || dim2() < 0) |
|
253 return 0; |
|
254 else |
|
255 return dimensions.numel (); |
|
256 } |
|
257 |
5275
|
258 octave_idx_type nelem (void) const { return capacity (); } |
|
259 octave_idx_type length (void) const { return numel (); } |
5164
|
260 |
5275
|
261 octave_idx_type dim1 (void) const { return dimensions(0); } |
|
262 octave_idx_type dim2 (void) const { return dimensions(1); } |
5164
|
263 |
5275
|
264 octave_idx_type rows (void) const { return dim1 (); } |
|
265 octave_idx_type cols (void) const { return dim2 (); } |
|
266 octave_idx_type columns (void) const { return dim2 (); } |
5164
|
267 |
5275
|
268 octave_idx_type get_row_index (octave_idx_type k) { return ridx (k); } |
|
269 octave_idx_type get_col_index (octave_idx_type k) |
5164
|
270 { |
5275
|
271 octave_idx_type ret = 0; |
5164
|
272 while (cidx(ret+1) < k) |
|
273 ret++; |
|
274 return ret; |
|
275 } |
5275
|
276 size_t byte_size (void) const { return (cols () + 1) * sizeof (octave_idx_type) + |
|
277 capacity () * (sizeof (T) + sizeof (octave_idx_type)); } |
5164
|
278 |
|
279 dim_vector dims (void) const { return dimensions; } |
|
280 |
|
281 Sparse<T> squeeze (void) const { return *this; } |
|
282 |
5275
|
283 octave_idx_type compute_index (const Array<octave_idx_type>& ra_idx) const; |
5164
|
284 |
5275
|
285 T range_error (const char *fcn, octave_idx_type n) const; |
|
286 T& range_error (const char *fcn, octave_idx_type n); |
5164
|
287 |
5275
|
288 T range_error (const char *fcn, octave_idx_type i, octave_idx_type j) const; |
|
289 T& range_error (const char *fcn, octave_idx_type i, octave_idx_type j); |
5164
|
290 |
5275
|
291 T range_error (const char *fcn, const Array<octave_idx_type>& ra_idx) const; |
|
292 T& range_error (const char *fcn, const Array<octave_idx_type>& ra_idx); |
5164
|
293 |
|
294 // No checking, even for multiple references, ever. |
|
295 |
5275
|
296 T& xelem (octave_idx_type n) |
5164
|
297 { |
5275
|
298 octave_idx_type i = n % rows (), j = n / rows(); |
5164
|
299 return xelem (i, j); |
|
300 } |
|
301 |
5275
|
302 T xelem (octave_idx_type n) const |
5164
|
303 { |
5275
|
304 octave_idx_type i = n % rows (), j = n / rows(); |
5164
|
305 return xelem (i, j); |
|
306 } |
|
307 |
5275
|
308 T& xelem (octave_idx_type i, octave_idx_type j) { return rep->elem (i, j); } |
|
309 T xelem (octave_idx_type i, octave_idx_type j) const { return rep->celem (i, j); } |
5164
|
310 |
5275
|
311 T& xelem (const Array<octave_idx_type>& ra_idx) |
5164
|
312 { return xelem (compute_index (ra_idx)); } |
|
313 |
5275
|
314 T xelem (const Array<octave_idx_type>& ra_idx) const |
5164
|
315 { return xelem (compute_index (ra_idx)); } |
|
316 |
5775
|
317 // FIXME -- would be nice to fix this so that we don't |
5164
|
318 // unnecessarily force a copy, but that is not so easy, and I see no |
|
319 // clean way to do it. |
|
320 |
5275
|
321 T& checkelem (octave_idx_type n) |
5164
|
322 { |
|
323 if (n < 0 || n >= numel ()) |
|
324 return range_error ("T& Sparse<T>::checkelem", n); |
|
325 else |
|
326 { |
|
327 make_unique (); |
|
328 return xelem (n); |
|
329 } |
|
330 } |
|
331 |
5275
|
332 T& checkelem (octave_idx_type i, octave_idx_type j) |
5164
|
333 { |
|
334 if (i < 0 || j < 0 || i >= dim1 () || j >= dim2 ()) |
|
335 return range_error ("T& Sparse<T>::checkelem", i, j); |
|
336 else |
|
337 { |
|
338 make_unique (); |
|
339 return xelem (i, j); |
|
340 } |
|
341 } |
|
342 |
5275
|
343 T& checkelem (const Array<octave_idx_type>& ra_idx) |
5164
|
344 { |
5275
|
345 octave_idx_type i = compute_index (ra_idx); |
5164
|
346 |
|
347 if (i < 0) |
|
348 return range_error ("T& Sparse<T>::checkelem", ra_idx); |
|
349 else |
|
350 return elem (i); |
|
351 } |
|
352 |
5275
|
353 T& elem (octave_idx_type n) |
5164
|
354 { |
|
355 make_unique (); |
|
356 return xelem (n); |
|
357 } |
|
358 |
5275
|
359 T& elem (octave_idx_type i, octave_idx_type j) |
5164
|
360 { |
|
361 make_unique (); |
|
362 return xelem (i, j); |
|
363 } |
|
364 |
5275
|
365 T& elem (const Array<octave_idx_type>& ra_idx) |
5164
|
366 { return Sparse<T>::elem (compute_index (ra_idx)); } |
|
367 |
|
368 #if defined (BOUNDS_CHECKING) |
5275
|
369 T& operator () (octave_idx_type n) { return checkelem (n); } |
|
370 T& operator () (octave_idx_type i, octave_idx_type j) { return checkelem (i, j); } |
|
371 T& operator () (const Array<octave_idx_type>& ra_idx) { return checkelem (ra_idx); } |
5164
|
372 #else |
5275
|
373 T& operator () (octave_idx_type n) { return elem (n); } |
|
374 T& operator () (octave_idx_type i, octave_idx_type j) { return elem (i, j); } |
|
375 T& operator () (const Array<octave_idx_type>& ra_idx) { return elem (ra_idx); } |
5164
|
376 #endif |
|
377 |
5275
|
378 T checkelem (octave_idx_type n) const |
5164
|
379 { |
|
380 if (n < 0 || n >= numel ()) |
|
381 return range_error ("T Sparse<T>::checkelem", n); |
|
382 else |
|
383 return xelem (n); |
|
384 } |
|
385 |
5275
|
386 T checkelem (octave_idx_type i, octave_idx_type j) const |
5164
|
387 { |
|
388 if (i < 0 || j < 0 || i >= dim1 () || j >= dim2 ()) |
|
389 return range_error ("T Sparse<T>::checkelem", i, j); |
|
390 else |
|
391 return xelem (i, j); |
|
392 } |
|
393 |
5275
|
394 T checkelem (const Array<octave_idx_type>& ra_idx) const |
5164
|
395 { |
5275
|
396 octave_idx_type i = compute_index (ra_idx); |
5164
|
397 |
|
398 if (i < 0) |
|
399 return range_error ("T Sparse<T>::checkelem", ra_idx); |
|
400 else |
|
401 return Sparse<T>::elem (i); |
|
402 } |
|
403 |
5275
|
404 T elem (octave_idx_type n) const { return xelem (n); } |
5164
|
405 |
5275
|
406 T elem (octave_idx_type i, octave_idx_type j) const { return xelem (i, j); } |
5164
|
407 |
5275
|
408 T elem (const Array<octave_idx_type>& ra_idx) const |
5164
|
409 { return Sparse<T>::elem (compute_index (ra_idx)); } |
|
410 |
|
411 #if defined (BOUNDS_CHECKING) |
5275
|
412 T operator () (octave_idx_type n) const { return checkelem (n); } |
|
413 T operator () (octave_idx_type i, octave_idx_type j) const { return checkelem (i, j); } |
|
414 T operator () (const Array<octave_idx_type>& ra_idx) const { return checkelem (ra_idx); } |
5164
|
415 #else |
5275
|
416 T operator () (octave_idx_type n) const { return elem (n); } |
|
417 T operator () (octave_idx_type i, octave_idx_type j) const { return elem (i, j); } |
|
418 T operator () (const Array<octave_idx_type>& ra_idx) const { return elem (ra_idx); } |
5164
|
419 #endif |
|
420 |
|
421 Sparse<T> maybe_compress (bool remove_zeros = false) |
|
422 { rep->maybe_compress (remove_zeros); return (*this); } |
|
423 |
|
424 Sparse<T> reshape (const dim_vector& new_dims) const; |
|
425 |
|
426 // !!! WARNING !!! -- the following resize_no_fill functions are |
|
427 // public because template friends don't work properly with versions |
|
428 // of gcc earlier than 3.3. You should use these functions only in |
|
429 // classes that are derived from Sparse<T>. |
|
430 |
|
431 // protected: |
|
432 |
5275
|
433 void resize_no_fill (octave_idx_type r, octave_idx_type c); |
5164
|
434 |
|
435 void resize_no_fill (const dim_vector& dv); |
|
436 |
|
437 public: |
5275
|
438 Sparse<T> permute (const Array<octave_idx_type>& vec, bool inv = false) const; |
5164
|
439 |
5275
|
440 Sparse<T> ipermute (const Array<octave_idx_type>& vec) const |
5164
|
441 { return permute (vec, true); } |
|
442 |
5275
|
443 void resize (octave_idx_type r, octave_idx_type c) { resize_no_fill (r, c); } |
5164
|
444 |
|
445 void resize (const dim_vector& dv) { resize_no_fill (dv); } |
|
446 |
5275
|
447 void change_capacity (octave_idx_type nz) { rep->change_length (nz); } |
5164
|
448 |
5275
|
449 Sparse<T>& insert (const Sparse<T>& a, octave_idx_type r, octave_idx_type c); |
|
450 Sparse<T>& insert (const Sparse<T>& a, const Array<octave_idx_type>& idx); |
5164
|
451 |
|
452 bool is_square (void) const { return (dim1 () == dim2 ()); } |
|
453 |
|
454 bool is_empty (void) const { return (rows () < 1 && cols () < 1); } |
|
455 |
|
456 Sparse<T> transpose (void) const; |
|
457 |
|
458 T* data (void) { make_unique (); return rep->d; } |
5275
|
459 T& data (octave_idx_type i) { make_unique (); return rep->data (i); } |
5164
|
460 T* xdata (void) { return rep->d; } |
5275
|
461 T& xdata (octave_idx_type i) { return rep->data (i); } |
5164
|
462 |
5275
|
463 T data (octave_idx_type i) const { return rep->data (i); } |
5900
|
464 // FIXME -- shouldn't this be returning const T*? |
5164
|
465 T* data (void) const { return rep->d; } |
|
466 |
5275
|
467 octave_idx_type* ridx (void) { make_unique (); return rep->r; } |
|
468 octave_idx_type& ridx (octave_idx_type i) { make_unique (); return rep->ridx (i); } |
|
469 octave_idx_type* xridx (void) { return rep->r; } |
|
470 octave_idx_type& xridx (octave_idx_type i) { return rep->ridx (i); } |
5164
|
471 |
5275
|
472 octave_idx_type ridx (octave_idx_type i) const { return rep->cridx (i); } |
5900
|
473 // FIXME -- shouldn't this be returning const octave_idx_type*? |
5275
|
474 octave_idx_type* ridx (void) const { return rep->r; } |
5164
|
475 |
5275
|
476 octave_idx_type* cidx (void) { make_unique (); return rep->c; } |
|
477 octave_idx_type& cidx (octave_idx_type i) { make_unique (); return rep->cidx (i); } |
|
478 octave_idx_type* xcidx (void) { return rep->c; } |
|
479 octave_idx_type& xcidx (octave_idx_type i) { return rep->cidx (i); } |
5164
|
480 |
5275
|
481 octave_idx_type cidx (octave_idx_type i) const { return rep->ccidx (i); } |
5900
|
482 // FIXME -- shouldn't this be returning const octave_idx_type*? |
5275
|
483 octave_idx_type* cidx (void) const { return rep->c; } |
5164
|
484 |
5275
|
485 octave_idx_type ndims (void) const { return dimensions.length (); } |
5164
|
486 |
|
487 void clear_index (void); |
|
488 |
|
489 void set_index (const idx_vector& i); |
|
490 |
5275
|
491 octave_idx_type index_count (void) const { return idx_count; } |
5164
|
492 |
|
493 idx_vector *get_idx (void) const { return idx; } |
|
494 |
|
495 void maybe_delete_elements (idx_vector& i); |
|
496 |
|
497 void maybe_delete_elements (idx_vector& i, idx_vector& j); |
|
498 |
|
499 void maybe_delete_elements (Array<idx_vector>& ra_idx); |
|
500 |
|
501 Sparse<T> value (void); |
|
502 |
|
503 Sparse<T> index (idx_vector& i, int resize_ok = 0) const; |
|
504 |
|
505 Sparse<T> index (idx_vector& i, idx_vector& j, int resize_ok = 0) const; |
|
506 |
|
507 Sparse<T> index (Array<idx_vector>& ra_idx, int resize_ok = 0) const; |
|
508 |
|
509 void print_info (std::ostream& os, const std::string& prefix) const; |
|
510 |
5900
|
511 // Unsafe. These functions exist to support the MEX interface. |
|
512 // You should not use them anywhere else. |
|
513 void *mex_get_data (void) const { return const_cast<T *> (data ()); } |
|
514 |
|
515 octave_idx_type *mex_get_ir (void) const { return const_cast<octave_idx_type *> (ridx ()); } |
|
516 |
|
517 octave_idx_type *mex_get_jc (void) const { return const_cast<octave_idx_type *> (cidx ()); } |
5164
|
518 }; |
|
519 |
|
520 // NOTE: these functions should be friends of the Sparse<T> class and |
|
521 // Sparse<T>::dimensions should be protected, not public, but we can't |
|
522 // do that because of bugs in gcc prior to 3.3. |
|
523 |
|
524 template <class LT, class RT> |
|
525 /* friend */ int |
|
526 assign (Sparse<LT>& lhs, const Sparse<RT>& rhs); |
|
527 |
|
528 template <class LT, class RT> |
|
529 /* friend */ int |
|
530 assign1 (Sparse<LT>& lhs, const Sparse<RT>& rhs); |
|
531 |
6708
|
532 #define INSTANTIATE_SPARSE_ASSIGN(LT, RT, API) \ |
|
533 template API int assign (Sparse<LT>&, const Sparse<RT>&); \ |
|
534 template API int assign1 (Sparse<LT>&, const Sparse<RT>&); |
5164
|
535 |
6708
|
536 #define INSTANTIATE_SPARSE(T, API) \ |
|
537 template class API Sparse<T>; |
5164
|
538 |
6708
|
539 #define INSTANTIATE_SPARSE_AND_ASSIGN(T, API) \ |
|
540 INSTANTIATE_SPARSE (T, API); \ |
|
541 INSTANTIATE_SPARSE_ASSIGN (T, T, API) |
5164
|
542 |
|
543 #endif |
|
544 |
|
545 /* |
|
546 ;;; Local Variables: *** |
|
547 ;;; mode: C++ *** |
|
548 ;;; End: *** |
|
549 */ |