1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1998-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if ! defined (octave_Sparse_h)
27 #define octave_Sparse_h 1
28 
29 #include "octave-config.h"
30 
31 #include <cassert>
32 #include <cstddef>
33 
34 #include <algorithm>
35 #include <iosfwd>
36 #include <string>
37 
38 #include "Array.h"
39 
40 class idx_vector;
41 class PermMatrix;
42 
43 // Two dimensional sparse class.  Handles the reference counting for
44 // all the derived classes.
45 
46 template <typename T>
47 class
48 Sparse
49 {
50 public:
51 
52   typedef T element_type;
53 
54 protected:
55   //--------------------------------------------------------------------
56   // The real representation of all Sparse arrays.
57   //--------------------------------------------------------------------
58 
59   class OCTAVE_API SparseRep
60   {
61   public:
62 
63     T *d;
64     octave_idx_type *r;
65     octave_idx_type *c;
66     octave_idx_type nzmx;
67     octave_idx_type nrows;
68     octave_idx_type ncols;
69     octave::refcount<octave_idx_type> count;
70 
SparseRep(void)71     SparseRep (void)
72       : d (new T [1]), r (new octave_idx_type [1] {}),
73         c (new octave_idx_type [1] {}),
74         nzmx (1), nrows (0), ncols (0), count (1)
75     { }
76 
SparseRep(octave_idx_type n)77     SparseRep (octave_idx_type n)
78       : d (new T [1]), r (new octave_idx_type [1] {}),
79         c (new octave_idx_type [n+1] {}),
80         nzmx (1), nrows (n), ncols (n), count (1)
81     { }
82 
83     SparseRep (octave_idx_type nr, octave_idx_type nc, octave_idx_type nz = 1)
84       : d (nz > 0 ? new T [nz] : new T [1]),
85         r (nz > 0 ? new octave_idx_type [nz] {} : new octave_idx_type [1] {}),
86         c (new octave_idx_type [nc+1] {}),
87         nzmx (nz > 0 ? nz : 1), nrows (nr), ncols (nc), count (1)
88     { }
89 
SparseRep(const SparseRep & a)90     SparseRep (const SparseRep& a)
91       : d (new T [a.nzmx]), r (new octave_idx_type [a.nzmx]),
92         c (new octave_idx_type [a.ncols + 1]),
93         nzmx (a.nzmx), nrows (a.nrows), ncols (a.ncols), count (1)
94     {
95       octave_idx_type nz = a.nnz ();
96       std::copy_n (a.d, nz, d);
97       std::copy_n (a.r, nz, r);
98       std::copy_n (a.c, ncols + 1, c);
99     }
100 
~SparseRep(void)101     ~SparseRep (void) { delete [] d; delete [] r; delete [] c; }
102 
length(void)103     octave_idx_type length (void) const { return nzmx; }
104 
nnz(void)105     octave_idx_type nnz (void) const { return c[ncols]; }
106 
107     T& elem (octave_idx_type _r, octave_idx_type _c);
108 
109     T celem (octave_idx_type _r, octave_idx_type _c) const;
110 
data(octave_idx_type i)111     T& data (octave_idx_type i) { return d[i]; }
112 
cdata(octave_idx_type i)113     T cdata (octave_idx_type i) const { return d[i]; }
114 
ridx(octave_idx_type i)115     octave_idx_type& ridx (octave_idx_type i) { return r[i]; }
116 
cridx(octave_idx_type i)117     octave_idx_type cridx (octave_idx_type i) const { return r[i]; }
118 
cidx(octave_idx_type i)119     octave_idx_type& cidx (octave_idx_type i) { return c[i]; }
120 
ccidx(octave_idx_type i)121     octave_idx_type ccidx (octave_idx_type i) const { return c[i]; }
122 
123     void maybe_compress (bool remove_zeros);
124 
125     void change_length (octave_idx_type nz);
126 
127     bool indices_ok (void) const;
128 
129     bool any_element_is_nan (void) const;
130 
131   private:
132 
133     // No assignment!
134 
135     SparseRep& operator = (const SparseRep& a);
136   };
137 
138   //--------------------------------------------------------------------
139 
make_unique(void)140   void make_unique (void)
141   {
142     if (rep->count > 1)
143       {
144         SparseRep *r = new SparseRep (*rep);
145 
146         if (--rep->count == 0)
147           delete rep;
148 
149         rep = r;
150       }
151   }
152 
153 public:
154 
155   // !!! WARNING !!! -- these should be protected, not public.  You
156   // should not access these data members directly!
157 
158   typename Sparse<T>::SparseRep *rep;
159 
160   dim_vector dimensions;
161 
162 private:
163 
164   static typename Sparse<T>::SparseRep *nil_rep (void);
165 
166 public:
167 
Sparse(void)168   Sparse (void)
169     : rep (nil_rep ()), dimensions (dim_vector (0,0))
170   {
171     rep->count++;
172   }
173 
Sparse(octave_idx_type n)174   explicit Sparse (octave_idx_type n)
175     : rep (new typename Sparse<T>::SparseRep (n)),
176       dimensions (dim_vector (n, n)) { }
177 
Sparse(octave_idx_type nr,octave_idx_type nc)178   explicit Sparse (octave_idx_type nr, octave_idx_type nc)
179     : rep (new typename Sparse<T>::SparseRep (nr, nc)),
180       dimensions (dim_vector (nr, nc)) { }
181 
182   explicit Sparse (octave_idx_type nr, octave_idx_type nc, T val);
183 
Sparse(const dim_vector & dv,octave_idx_type nz)184   Sparse (const dim_vector& dv, octave_idx_type nz)
185     : rep (new typename Sparse<T>::SparseRep (dv(0), dv(1), nz)),
186       dimensions (dv) { }
187 
Sparse(octave_idx_type nr,octave_idx_type nc,octave_idx_type nz)188   Sparse (octave_idx_type nr, octave_idx_type nc, octave_idx_type nz)
189     : rep (new typename Sparse<T>::SparseRep (nr, nc, nz)),
190       dimensions (dim_vector (nr, nc)) { }
191 
192   // Both SparseMatrix and SparseBoolMatrix need this ctor, and this
193   // is their only common ancestor.
194   explicit Sparse (const PermMatrix& a);
195 
196   // Type conversion case.  Preserves nzmax.
197   template <typename U>
Sparse(const Sparse<U> & a)198   Sparse (const Sparse<U>& a)
199     : rep (new typename Sparse<T>::SparseRep (a.rep->nrows, a.rep->ncols,
200                                               a.rep->nzmx)),
201       dimensions (a.dimensions)
202   {
203     octave_idx_type nz = a.nnz ();
204     std::copy_n (a.rep->d, nz, rep->d);
205     std::copy_n (a.rep->r, nz, rep->r);
206     std::copy_n (a.rep->c, rep->ncols + 1, rep->c);
207   }
208 
209   // No type conversion case.
Sparse(const Sparse<T> & a)210   Sparse (const Sparse<T>& a)
211     : rep (a.rep), dimensions (a.dimensions)
212   {
213     rep->count++;
214   }
215 
216 public:
217 
218   Sparse (const dim_vector& dv);
219 
220   Sparse (const Sparse<T>& a, const dim_vector& dv);
221 
222   Sparse (const Array<T>& a, const idx_vector& r, const idx_vector& c,
223           octave_idx_type nr = -1, octave_idx_type nc = -1,
224           bool sum_terms = true, octave_idx_type nzm = -1);
225 
226   // Sparsify a normal matrix
227   Sparse (const Array<T>& a);
228 
229   virtual ~Sparse (void);
230 
231   Sparse<T>& operator = (const Sparse<T>& a);
232 
233   //! Amount of storage for nonzero elements.
234   //! This may differ from the actual number of elements, see nnz().
nzmax(void)235   octave_idx_type nzmax (void) const { return rep->length (); }
236 
237   //! Actual number of nonzero terms.
nnz(void)238   octave_idx_type nnz (void) const { return rep->nnz (); }
239 
240   // Querying the number of elements (incl. zeros) may overflow the index type,
241   // so don't do it unless you really need it.
numel(void)242   octave_idx_type numel (void) const
243   {
244     return dimensions.safe_numel ();
245   }
246 
dim1(void)247   octave_idx_type dim1 (void) const { return dimensions(0); }
dim2(void)248   octave_idx_type dim2 (void) const { return dimensions(1); }
249 
rows(void)250   octave_idx_type rows (void) const { return dim1 (); }
cols(void)251   octave_idx_type cols (void) const { return dim2 (); }
columns(void)252   octave_idx_type columns (void) const { return dim2 (); }
253 
get_row_index(octave_idx_type k)254   octave_idx_type get_row_index (octave_idx_type k) { return ridx (k); }
get_col_index(octave_idx_type k)255   octave_idx_type get_col_index (octave_idx_type k)
256   {
257     octave_idx_type ret = 0;
258     while (cidx (ret+1) < k)
259       ret++;
260     return ret;
261   }
262 
byte_size(void)263   std::size_t byte_size (void) const
264   {
265     return (static_cast<std::size_t> (cols () + 1) * sizeof (octave_idx_type)
266             + static_cast<std::size_t> (nzmax ())
267             * (sizeof (T) + sizeof (octave_idx_type)));
268   }
269 
dims(void)270   dim_vector dims (void) const { return dimensions; }
271 
squeeze(void)272   Sparse<T> squeeze (void) const { return *this; }
273 
274   octave_idx_type compute_index (const Array<octave_idx_type>& ra_idx) const;
275 
276   // FIXME: Functions are marked as NORETURN, but they are used with
277   //        a return statement in following code.  Shouldn't that be fixed?
278   OCTAVE_NORETURN T range_error (const char *fcn, octave_idx_type n) const;
279   OCTAVE_NORETURN T& range_error (const char *fcn, octave_idx_type n);
280 
281   OCTAVE_NORETURN T range_error (const char *fcn,
282                                  octave_idx_type i, octave_idx_type j) const;
283   OCTAVE_NORETURN T& range_error (const char *fcn,
284                                   octave_idx_type i, octave_idx_type j);
285 
286   OCTAVE_NORETURN T range_error (const char *fcn,
287                                  const Array<octave_idx_type>& ra_idx) const;
288   OCTAVE_NORETURN T& range_error (const char *fcn,
289                                   const Array<octave_idx_type>& ra_idx);
290 
291   // No checking, even for multiple references, ever.
292 
xelem(octave_idx_type n)293   T& xelem (octave_idx_type n)
294   {
295     octave_idx_type i = n % rows ();
296     octave_idx_type j = n / rows ();
297     return xelem (i, j);
298   }
299 
xelem(octave_idx_type n)300   T xelem (octave_idx_type n) const
301   {
302     octave_idx_type i = n % rows ();
303     octave_idx_type j = n / rows ();
304     return xelem (i, j);
305   }
306 
xelem(octave_idx_type i,octave_idx_type j)307   T& xelem (octave_idx_type i, octave_idx_type j) { return rep->elem (i, j); }
xelem(octave_idx_type i,octave_idx_type j)308   T xelem (octave_idx_type i, octave_idx_type j) const
309   {
310     return rep->celem (i, j);
311   }
312 
xelem(const Array<octave_idx_type> & ra_idx)313   T& xelem (const Array<octave_idx_type>& ra_idx)
314   { return xelem (compute_index (ra_idx)); }
315 
xelem(const Array<octave_idx_type> & ra_idx)316   T xelem (const Array<octave_idx_type>& ra_idx) const
317   { return xelem (compute_index (ra_idx)); }
318 
319   // FIXME: would be nice to fix this so that we don't unnecessarily force a
320   // copy, but that is not so easy, and I see no clean way to do it.
321 
checkelem(octave_idx_type n)322   T& checkelem (octave_idx_type n)
323   {
324     if (n < 0 || n >= numel ())
325       // FIXME: Why should we "return" when range_error is OCTAVE_NORETURN?
326       return range_error ("T& Sparse<T>::checkelem", n);
327     else
328       {
329         make_unique ();
330         return xelem (n);
331       }
332   }
333 
checkelem(octave_idx_type i,octave_idx_type j)334   T& checkelem (octave_idx_type i, octave_idx_type j)
335   {
336     if (i < 0 || j < 0 || i >= dim1 () || j >= dim2 ())
337       return range_error ("T& Sparse<T>::checkelem", i, j);
338     else
339       {
340         make_unique ();
341         return xelem (i, j);
342       }
343   }
344 
checkelem(const Array<octave_idx_type> & ra_idx)345   T& checkelem (const Array<octave_idx_type>& ra_idx)
346   {
347     octave_idx_type i = compute_index (ra_idx);
348 
349     if (i < 0)
350       return range_error ("T& Sparse<T>::checkelem", ra_idx);
351     else
352       return elem (i);
353   }
354 
elem(octave_idx_type n)355   T& elem (octave_idx_type n)
356   {
357     make_unique ();
358     return xelem (n);
359   }
360 
elem(octave_idx_type i,octave_idx_type j)361   T& elem (octave_idx_type i, octave_idx_type j)
362   {
363     make_unique ();
364     return xelem (i, j);
365   }
366 
elem(const Array<octave_idx_type> & ra_idx)367   T& elem (const Array<octave_idx_type>& ra_idx)
368   { return Sparse<T>::elem (compute_index (ra_idx)); }
369 
operator()370   T& operator () (octave_idx_type n)
371   {
372     return elem (n);
373   }
374 
operator()375   T& operator () (octave_idx_type i, octave_idx_type j)
376   {
377     return elem (i, j);
378   }
379 
operator()380   T& operator () (const Array<octave_idx_type>& ra_idx)
381   {
382     return elem (ra_idx);
383   }
384 
checkelem(octave_idx_type n)385   T checkelem (octave_idx_type n) const
386   {
387     if (n < 0 || n >= numel ())
388       return range_error ("T Sparse<T>::checkelem", n);
389     else
390       return xelem (n);
391   }
392 
checkelem(octave_idx_type i,octave_idx_type j)393   T checkelem (octave_idx_type i, octave_idx_type j) const
394   {
395     if (i < 0 || j < 0 || i >= dim1 () || j >= dim2 ())
396       return range_error ("T Sparse<T>::checkelem", i, j);
397     else
398       return xelem (i, j);
399   }
400 
checkelem(const Array<octave_idx_type> & ra_idx)401   T checkelem (const Array<octave_idx_type>& ra_idx) const
402   {
403     octave_idx_type i = compute_index (ra_idx);
404 
405     if (i < 0)
406       return range_error ("T Sparse<T>::checkelem", ra_idx);
407     else
408       return Sparse<T>::elem (i);
409   }
410 
elem(octave_idx_type n)411   T elem (octave_idx_type n) const { return xelem (n); }
412 
elem(octave_idx_type i,octave_idx_type j)413   T elem (octave_idx_type i, octave_idx_type j) const { return xelem (i, j); }
414 
elem(const Array<octave_idx_type> & ra_idx)415   T elem (const Array<octave_idx_type>& ra_idx) const
416   { return Sparse<T>::elem (compute_index (ra_idx)); }
417 
operator()418   T operator () (octave_idx_type n) const { return elem (n); }
419 
operator()420   T operator () (octave_idx_type i, octave_idx_type j) const
421   {
422     return elem (i, j);
423   }
424 
operator()425   T operator () (const Array<octave_idx_type>& ra_idx) const
426   {
427     return elem (ra_idx);
428   }
429 
430   Sparse<T> maybe_compress (bool remove_zeros = false)
431   {
432     if (remove_zeros)
433       make_unique ();  // Need to unshare because elements are removed.
434 
435     rep->maybe_compress (remove_zeros);
436     return (*this);
437   }
438 
439   Sparse<T> reshape (const dim_vector& new_dims) const;
440 
441   Sparse<T> permute (const Array<octave_idx_type>& vec, bool inv = false) const;
442 
ipermute(const Array<octave_idx_type> & vec)443   Sparse<T> ipermute (const Array<octave_idx_type>& vec) const
444   {
445     return permute (vec, true);
446   }
447 
448   void resize1 (octave_idx_type n);
449 
450   void resize (octave_idx_type r, octave_idx_type c);
451 
452   void resize (const dim_vector& dv);
453 
change_capacity(octave_idx_type nz)454   void change_capacity (octave_idx_type nz)
455   {
456     if (nz < nnz ())
457       make_unique ();  // Unshare now because elements will be truncated.
458     rep->change_length (nz);
459   }
460 
461   Sparse<T>& insert (const Sparse<T>& a, octave_idx_type r, octave_idx_type c);
462   Sparse<T>& insert (const Sparse<T>& a, const Array<octave_idx_type>& idx);
463 
issquare(void)464   bool issquare (void) const { return (dim1 () == dim2 ()); }
465 
isempty(void)466   bool isempty (void) const { return (rows () < 1 || cols () < 1); }
467 
468   Sparse<T> transpose (void) const;
469 
data(void)470   T * data (void) { make_unique (); return rep->d; }
data(octave_idx_type i)471   T& data (octave_idx_type i) { make_unique (); return rep->data (i); }
xdata(void)472   T * xdata (void) { return rep->d; }
xdata(octave_idx_type i)473   T& xdata (octave_idx_type i) { return rep->data (i); }
474 
data(octave_idx_type i)475   T data (octave_idx_type i) const { return rep->data (i); }
476   // FIXME: shouldn't this be returning const T*?
data(void)477   T * data (void) const { return rep->d; }
478 
ridx(void)479   octave_idx_type * ridx (void) { make_unique (); return rep->r; }
ridx(octave_idx_type i)480   octave_idx_type& ridx (octave_idx_type i)
481   {
482     make_unique (); return rep->ridx (i);
483   }
484 
xridx(void)485   octave_idx_type * xridx (void) { return rep->r; }
xridx(octave_idx_type i)486   octave_idx_type& xridx (octave_idx_type i) { return rep->ridx (i); }
487 
ridx(octave_idx_type i)488   octave_idx_type ridx (octave_idx_type i) const { return rep->cridx (i); }
489   // FIXME: shouldn't this be returning const octave_idx_type*?
ridx(void)490   octave_idx_type * ridx (void) const { return rep->r; }
491 
cidx(void)492   octave_idx_type * cidx (void) { make_unique (); return rep->c; }
cidx(octave_idx_type i)493   octave_idx_type& cidx (octave_idx_type i)
494   {
495     make_unique (); return rep->cidx (i);
496   }
497 
xcidx(void)498   octave_idx_type * xcidx (void) { return rep->c; }
xcidx(octave_idx_type i)499   octave_idx_type& xcidx (octave_idx_type i) { return rep->cidx (i); }
500 
cidx(octave_idx_type i)501   octave_idx_type cidx (octave_idx_type i) const { return rep->ccidx (i); }
502   // FIXME: shouldn't this be returning const octave_idx_type*?
cidx(void)503   octave_idx_type * cidx (void) const { return rep->c; }
504 
ndims(void)505   octave_idx_type ndims (void) const { return dimensions.ndims (); }
506 
507   void delete_elements (const idx_vector& i);
508 
509   void delete_elements (int dim, const idx_vector& i);
510 
511   void delete_elements (const idx_vector& i, const idx_vector& j);
512 
513   Sparse<T> index (const idx_vector& i, bool resize_ok = false) const;
514 
515   Sparse<T> index (const idx_vector& i, const idx_vector& j,
516                    bool resize_ok = false) const;
517 
518   void assign (const idx_vector& i, const Sparse<T>& rhs);
519 
520   void assign (const idx_vector& i, const idx_vector& j, const Sparse<T>& rhs);
521 
522   void print_info (std::ostream& os, const std::string& prefix) const;
523 
524   // Unsafe.  These functions exist to support the MEX interface.
525   // You should not use them anywhere else.
mex_get_data(void)526   void * mex_get_data (void) const { return const_cast<T *> (data ()); }
527 
mex_get_ir(void)528   octave_idx_type * mex_get_ir (void) const
529   {
530     return const_cast<octave_idx_type *> (ridx ());
531   }
532 
mex_get_jc(void)533   octave_idx_type * mex_get_jc (void) const
534   {
535     return const_cast<octave_idx_type *> (cidx ());
536   }
537 
538   Sparse<T> sort (octave_idx_type dim = 0, sortmode mode = ASCENDING) const;
539   Sparse<T> sort (Array<octave_idx_type> &sidx, octave_idx_type dim = 0,
540                   sortmode mode = ASCENDING) const;
541 
542   Sparse<T> diag (octave_idx_type k = 0) const;
543 
544   // dim = -1 and dim = -2 are special; see Array<T>::cat description.
545   static Sparse<T>
546   cat (int dim, octave_idx_type n, const Sparse<T> *sparse_list);
547 
548   Array<T> array_value (void) const;
549 
550   // Generic any/all test functionality with arbitrary predicate.
551   template <typename F, bool zero>
test(F fcn)552   bool test (F fcn) const
553   {
554     return any_all_test<F, T, zero> (fcn, data (), nnz ());
555   }
556 
557   // Simpler calls.
558   template <typename F>
test_any(F fcn)559   bool test_any (F fcn) const
560   { return test<F, false> (fcn); }
561 
562   template <typename F>
test_all(F fcn)563   bool test_all (F fcn) const
564   { return test<F, true> (fcn); }
565 
566   // Overloads for function references.
test_any(bool (& fcn)(T))567   bool test_any (bool (&fcn) (T)) const
568   { return test<bool (&) (T), false> (fcn); }
569 
test_any(bool (& fcn)(const T &))570   bool test_any (bool (&fcn) (const T&)) const
571   { return test<bool (&) (const T&), false> (fcn); }
572 
test_all(bool (& fcn)(T))573   bool test_all (bool (&fcn) (T)) const
574   { return test<bool (&) (T), true> (fcn); }
575 
test_all(bool (& fcn)(const T &))576   bool test_all (bool (&fcn) (const T&)) const
577   { return test<bool (&) (const T&), true> (fcn); }
578 
579   template <typename U, typename F>
580   Sparse<U>
map(F fcn)581   map (F fcn) const
582   {
583     Sparse<U> result;
584     U f_zero = fcn (0.0);
585 
586     if (f_zero != 0.0)
587       {
588         octave_idx_type nr = rows ();
589         octave_idx_type nc = cols ();
590 
591         result = Sparse<U> (nr, nc, f_zero);
592 
593         for (octave_idx_type j = 0; j < nc; j++)
594           for (octave_idx_type i = cidx (j); i < cidx (j+1); i++)
595             {
596               octave_quit ();
597               /* Use data instead of elem for better performance. */
598               result.data (ridx (i) + j * nr) = fcn (data (i));
599             }
600 
601         result.maybe_compress (true);
602       }
603     else
604       {
605         octave_idx_type nz = nnz ();
606         octave_idx_type nr = rows ();
607         octave_idx_type nc = cols ();
608 
609         result = Sparse<U> (nr, nc, nz);
610         octave_idx_type ii = 0;
611         result.cidx (ii) = 0;
612 
613         for (octave_idx_type j = 0; j < nc; j++)
614           {
615             for (octave_idx_type i = cidx (j); i < cidx (j+1); i++)
616               {
617                 U val = fcn (data (i));
618                 if (val != 0.0)
619                   {
620                     result.data (ii) = val;
621                     result.ridx (ii++) = ridx (i);
622                   }
623                 octave_quit ();
624               }
625             result.cidx (j+1) = ii;
626           }
627 
628         result.maybe_compress (false);
629       }
630 
631     return result;
632   }
633 
634   // Overloads for function references.
635   template <typename U>
636   Sparse<U>
map(U (& fcn)(T))637   map (U (&fcn) (T)) const
638   { return map<U, U (&) (T)> (fcn); }
639 
640   template <typename U>
641   Sparse<U>
map(U (& fcn)(const T &))642   map (U (&fcn) (const T&)) const
643   { return map<U, U (&) (const T&)> (fcn); }
644 
indices_ok(void)645   bool indices_ok (void) const { return rep->indices_ok (); }
646 
any_element_is_nan(void)647   bool any_element_is_nan (void) const
648   { return rep->any_element_is_nan (); }
649 };
650 
651 template <typename T>
652 std::istream&
653 read_sparse_matrix (std::istream& is, Sparse<T>& a,
654                     T (*read_fcn) (std::istream&));
655 
656 #endif
657