GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
Sparse-op-defs.h
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1998-2024 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_op_defs_h)
27 #define octave_Sparse_op_defs_h 1
28 
29 #include "octave-config.h"
30 
31 #include "Array-util.h"
32 #include "lo-array-errwarn.h"
33 #include "mx-inlines.cc"
34 #include "oct-locbuf.h"
35 
36 // sparse matrix by scalar operations.
37 
38 #define SPARSE_SMS_BIN_OP_1(R, F, OP, M, S) \
39  R \
40  F (const M& m, const S& s) \
41  { \
42  octave_idx_type nr = m.rows (); \
43  octave_idx_type nc = m.cols (); \
44  \
45  R r (nr, nc, (0.0 OP s)); \
46  \
47  for (octave_idx_type j = 0; j < nc; j++) \
48  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++) \
49  r.xelem (m.ridx (i), j) = m.data (i) OP s; \
50  return r; \
51  }
52 
53 #define SPARSE_SMS_BIN_OP_2(R, F, OP, M, S) \
54  R \
55  F (const M& m, const S& s) \
56  { \
57  octave_idx_type nr = m.rows (); \
58  octave_idx_type nc = m.cols (); \
59  octave_idx_type nz = m.nnz (); \
60  \
61  R r (nr, nc, nz); \
62  \
63  for (octave_idx_type i = 0; i < nz; i++) \
64  { \
65  r.xdata (i) = m.data (i) OP s; \
66  r.xridx (i) = m.ridx (i); \
67  } \
68  for (octave_idx_type i = 0; i < nc + 1; i++) \
69  r.xcidx (i) = m.cidx (i); \
70  \
71  r.maybe_compress (true); \
72  return r; \
73  }
74 
75 #define SPARSE_SMS_BIN_OPS(R1, R2, M, S) \
76  SPARSE_SMS_BIN_OP_1 (R1, operator +, +, M, S) \
77  SPARSE_SMS_BIN_OP_1 (R1, operator -, -, M, S) \
78  SPARSE_SMS_BIN_OP_2 (R2, operator *, *, M, S) \
79  SPARSE_SMS_BIN_OP_2 (R2, operator /, /, M, S)
80 
81 #define SPARSE_SMS_CMP_OP(F, OP, M, S) \
82  SparseBoolMatrix \
83  F (const M& m, const S& s) \
84  { \
85  octave_idx_type nr = m.rows (); \
86  octave_idx_type nc = m.cols (); \
87  SparseBoolMatrix r; \
88  \
89  M::element_type m_zero = M::element_type (); \
90  \
91  if (m_zero OP s) \
92  { \
93  r = SparseBoolMatrix (nr, nc, true); \
94  for (octave_idx_type j = 0; j < nc; j++) \
95  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++) \
96  if (! (m.data (i) OP s)) \
97  r.data (m.ridx (i) + j * nr) = false; \
98  r.maybe_compress (true); \
99  } \
100  else \
101  { \
102  r = SparseBoolMatrix (nr, nc, m.nnz ()); \
103  r.cidx (0) = static_cast<octave_idx_type> (0); \
104  octave_idx_type nel = 0; \
105  for (octave_idx_type j = 0; j < nc; j++) \
106  { \
107  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++) \
108  if (m.data (i) OP s) \
109  { \
110  r.ridx (nel) = m.ridx (i); \
111  r.data (nel++) = true; \
112  } \
113  r.cidx (j + 1) = nel; \
114  } \
115  r.maybe_compress (false); \
116  } \
117  return r; \
118  }
119 
120 #define SPARSE_SMS_CMP_OPS(M, S) \
121  SPARSE_SMS_CMP_OP (mx_el_lt, <, M, S) \
122  SPARSE_SMS_CMP_OP (mx_el_le, <=, M, S) \
123  SPARSE_SMS_CMP_OP (mx_el_ge, >=, M, S) \
124  SPARSE_SMS_CMP_OP (mx_el_gt, >, M, S) \
125  SPARSE_SMS_CMP_OP (mx_el_eq, ==, M, S) \
126  SPARSE_SMS_CMP_OP (mx_el_ne, !=, M, S)
127 
128 #define SPARSE_SMS_EQNE_OPS(M, S) \
129  SPARSE_SMS_CMP_OP (mx_el_eq, ==, M, S) \
130  SPARSE_SMS_CMP_OP (mx_el_ne, !=, M, S)
131 
132 #define SPARSE_SMS_BOOL_OR_OP(M, S) \
133  SparseBoolMatrix \
134  mx_el_or (const M& m, const S& s) \
135  { \
136  octave_idx_type nr = m.rows (); \
137  octave_idx_type nc = m.cols (); \
138  SparseBoolMatrix r; \
139  \
140  M::element_type lhs_zero = M::element_type (); \
141  S rhs_zero = S (); \
142  \
143  if (nr > 0 && nc > 0) \
144  { \
145  if (s != rhs_zero) \
146  r = SparseBoolMatrix (nr, nc, true); \
147  else \
148  { \
149  r = SparseBoolMatrix (nr, nc, m.nnz ()); \
150  r.cidx (0) = static_cast<octave_idx_type> (0); \
151  octave_idx_type nel = 0; \
152  for (octave_idx_type j = 0; j < nc; j++) \
153  { \
154  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++) \
155  if (m.data (i) != lhs_zero) \
156  { \
157  r.ridx (nel) = m.ridx (i); \
158  r.data (nel++) = true; \
159  } \
160  r.cidx (j + 1) = nel; \
161  } \
162  r.maybe_compress (false); \
163  } \
164  } \
165  return r; \
166  }
167 
168 #define SPARSE_SMS_BOOL_AND_OP(M, S) \
169  SparseBoolMatrix \
170  mx_el_and (const M& m, const S& s) \
171  { \
172  octave_idx_type nr = m.rows (); \
173  octave_idx_type nc = m.cols (); \
174  SparseBoolMatrix r; \
175  \
176  M::element_type lhs_zero = M::element_type (); \
177  S rhs_zero = S (); \
178  \
179  if (nr > 0 && nc > 0) \
180  { \
181  if (s != rhs_zero) \
182  { \
183  r = SparseBoolMatrix (nr, nc, m.nnz ()); \
184  r.cidx (0) = static_cast<octave_idx_type> (0); \
185  octave_idx_type nel = 0; \
186  for (octave_idx_type j = 0; j < nc; j++) \
187  { \
188  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++) \
189  if (m.data (i) != lhs_zero) \
190  { \
191  r.ridx (nel) = m.ridx (i); \
192  r.data (nel++) = true; \
193  } \
194  r.cidx (j + 1) = nel; \
195  } \
196  r.maybe_compress (false); \
197  } \
198  else \
199  r = SparseBoolMatrix (nr, nc); \
200  } \
201  return r; \
202  }
203 
204 #define SPARSE_SMS_BOOL_OPS(M, S) \
205  SPARSE_SMS_BOOL_AND_OP (M, S) \
206  SPARSE_SMS_BOOL_OR_OP (M, S)
207 
208 // scalar by sparse matrix operations.
209 
210 #define SPARSE_SSM_BIN_OP_1(R, F, OP, S, M) \
211  R \
212  F (const S& s, const M& m) \
213  { \
214  octave_idx_type nr = m.rows (); \
215  octave_idx_type nc = m.cols (); \
216  \
217  R r (nr, nc, (s OP 0.0)); \
218  \
219  for (octave_idx_type j = 0; j < nc; j++) \
220  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++) \
221  r.xelem (m.ridx (i), j) = s OP m.data (i); \
222  \
223  return r; \
224  }
225 
226 #define SPARSE_SSM_BIN_OP_2(R, F, OP, S, M) \
227  R \
228  F (const S& s, const M& m) \
229  { \
230  octave_idx_type nr = m.rows (); \
231  octave_idx_type nc = m.cols (); \
232  octave_idx_type nz = m.nnz (); \
233  \
234  R r (nr, nc, nz); \
235  \
236  for (octave_idx_type i = 0; i < nz; i++) \
237  { \
238  r.xdata (i) = s OP m.data (i); \
239  r.xridx (i) = m.ridx (i); \
240  } \
241  for (octave_idx_type i = 0; i < nc + 1; i++) \
242  r.xcidx (i) = m.cidx (i); \
243  \
244  r.maybe_compress(true); \
245  return r; \
246  }
247 
248 #define SPARSE_SSM_BIN_OPS(R1, R2, S, M) \
249  SPARSE_SSM_BIN_OP_1 (R1, operator +, +, S, M) \
250  SPARSE_SSM_BIN_OP_1 (R1, operator -, -, S, M) \
251  SPARSE_SSM_BIN_OP_2 (R2, operator *, *, S, M) \
252  SPARSE_SSM_BIN_OP_2 (R2, operator /, /, S, M)
253 
254 #define SPARSE_SSM_CMP_OP(F, OP, S, M) \
255  SparseBoolMatrix \
256  F (const S& s, const M& m) \
257  { \
258  octave_idx_type nr = m.rows (); \
259  octave_idx_type nc = m.cols (); \
260  SparseBoolMatrix r; \
261  \
262  M::element_type m_zero = M::element_type (); \
263  \
264  if (s OP m_zero) \
265  { \
266  r = SparseBoolMatrix (nr, nc, true); \
267  for (octave_idx_type j = 0; j < nc; j++) \
268  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++) \
269  if (! (s OP m.data (i))) \
270  r.data (m.ridx (i) + j * nr) = false; \
271  r.maybe_compress (true); \
272  } \
273  else \
274  { \
275  r = SparseBoolMatrix (nr, nc, m.nnz ()); \
276  r.cidx (0) = static_cast<octave_idx_type> (0); \
277  octave_idx_type nel = 0; \
278  for (octave_idx_type j = 0; j < nc; j++) \
279  { \
280  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++) \
281  if (s OP m.data (i)) \
282  { \
283  r.ridx (nel) = m.ridx (i); \
284  r.data (nel++) = true; \
285  } \
286  r.cidx (j + 1) = nel; \
287  } \
288  r.maybe_compress (false); \
289  } \
290  return r; \
291  }
292 
293 #define SPARSE_SSM_CMP_OPS(S, M) \
294  SPARSE_SSM_CMP_OP (mx_el_lt, <, S, M) \
295  SPARSE_SSM_CMP_OP (mx_el_le, <=, S, M) \
296  SPARSE_SSM_CMP_OP (mx_el_ge, >=, S, M) \
297  SPARSE_SSM_CMP_OP (mx_el_gt, >, S, M) \
298  SPARSE_SSM_CMP_OP (mx_el_eq, ==, S, M) \
299  SPARSE_SSM_CMP_OP (mx_el_ne, !=, S, M)
300 
301 #define SPARSE_SSM_EQNE_OPS(S, M) \
302  SPARSE_SSM_CMP_OP (mx_el_eq, ==, S, M) \
303  SPARSE_SSM_CMP_OP (mx_el_ne, !=, S, M)
304 
305 #define SPARSE_SSM_BOOL_OR_OP(S, M) \
306  SparseBoolMatrix \
307  mx_el_or (const S& s, const M& m) \
308  { \
309  octave_idx_type nr = m.rows (); \
310  octave_idx_type nc = m.cols (); \
311  SparseBoolMatrix r; \
312  \
313  S lhs_zero = S (); \
314  M::element_type rhs_zero = M::element_type (); \
315  \
316  if (nr > 0 && nc > 0) \
317  { \
318  if (s != lhs_zero) \
319  r = SparseBoolMatrix (nr, nc, true); \
320  else \
321  { \
322  r = SparseBoolMatrix (nr, nc, m.nnz ()); \
323  r.cidx (0) = static_cast<octave_idx_type> (0); \
324  octave_idx_type nel = 0; \
325  for (octave_idx_type j = 0; j < nc; j++) \
326  { \
327  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++) \
328  if (m.data (i) != rhs_zero) \
329  { \
330  r.ridx (nel) = m.ridx (i); \
331  r.data (nel++) = true; \
332  } \
333  r.cidx (j + 1) = nel; \
334  } \
335  r.maybe_compress (false); \
336  } \
337  } \
338  return r; \
339  }
340 
341 #define SPARSE_SSM_BOOL_AND_OP(S, M) \
342  SparseBoolMatrix \
343  mx_el_and (const S& s, const M& m) \
344  { \
345  octave_idx_type nr = m.rows (); \
346  octave_idx_type nc = m.cols (); \
347  SparseBoolMatrix r; \
348  \
349  S lhs_zero = S (); \
350  M::element_type rhs_zero = M::element_type (); \
351  \
352  if (nr > 0 && nc > 0) \
353  { \
354  if (s != lhs_zero) \
355  { \
356  r = SparseBoolMatrix (nr, nc, m.nnz ()); \
357  r.cidx (0) = static_cast<octave_idx_type> (0); \
358  octave_idx_type nel = 0; \
359  for (octave_idx_type j = 0; j < nc; j++) \
360  { \
361  for (octave_idx_type i = m.cidx (j); i < m.cidx (j+1); i++) \
362  if (m.data (i) != rhs_zero) \
363  { \
364  r.ridx (nel) = m.ridx (i); \
365  r.data (nel++) = true; \
366  } \
367  r.cidx (j + 1) = nel; \
368  } \
369  r.maybe_compress (false); \
370  } \
371  else \
372  r = SparseBoolMatrix (nr, nc); \
373  } \
374  return r; \
375  }
376 
377 #define SPARSE_SSM_BOOL_OPS(S, M) \
378  SPARSE_SSM_BOOL_AND_OP (S, M) \
379  SPARSE_SSM_BOOL_OR_OP (S, M)
380 
381 // sparse matrix by sparse matrix operations.
382 
383 #define SPARSE_SMSM_BIN_OP_1(R, F, OP, M1, M2) \
384  R \
385  F (const M1& m1, const M2& m2) \
386  { \
387  R r; \
388  \
389  octave_idx_type m1_nr = m1.rows (); \
390  octave_idx_type m1_nc = m1.cols (); \
391  \
392  octave_idx_type m2_nr = m2.rows (); \
393  octave_idx_type m2_nc = m2.cols (); \
394  \
395  if (m1_nr == 1 && m1_nc == 1) \
396  { \
397  if (m1.elem (0,0) == 0.) \
398  r = OP R (m2); \
399  else \
400  { \
401  r = R (m2_nr, m2_nc, m1.data (0) OP 0.); \
402  \
403  for (octave_idx_type j = 0 ; j < m2_nc ; j++) \
404  { \
405  octave_quit (); \
406  octave_idx_type idxj = j * m2_nr; \
407  for (octave_idx_type i = m2.cidx (j) ; i < m2.cidx (j+1) ; i++) \
408  { \
409  octave_quit (); \
410  r.data (idxj + m2.ridx (i)) = m1.data (0) OP m2.data (i); \
411  } \
412  } \
413  r.maybe_compress (); \
414  } \
415  } \
416  else if (m2_nr == 1 && m2_nc == 1) \
417  { \
418  if (m2.elem (0,0) == 0.) \
419  r = R (m1); \
420  else \
421  { \
422  r = R (m1_nr, m1_nc, 0. OP m2.data (0)); \
423  \
424  for (octave_idx_type j = 0 ; j < m1_nc ; j++) \
425  { \
426  octave_quit (); \
427  octave_idx_type idxj = j * m1_nr; \
428  for (octave_idx_type i = m1.cidx (j) ; i < m1.cidx (j+1) ; i++) \
429  { \
430  octave_quit (); \
431  r.data (idxj + m1.ridx (i)) = m1.data (i) OP m2.data (0); \
432  } \
433  } \
434  r.maybe_compress (); \
435  } \
436  } \
437  else if (m1_nr != m2_nr || m1_nc != m2_nc) \
438  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
439  else \
440  { \
441  r = R (m1_nr, m1_nc, (m1.nnz () + m2.nnz ())); \
442  \
443  octave_idx_type jx = 0; \
444  r.cidx (0) = 0; \
445  for (octave_idx_type i = 0 ; i < m1_nc ; i++) \
446  { \
447  octave_idx_type ja = m1.cidx (i); \
448  octave_idx_type ja_max = m1.cidx (i+1); \
449  bool ja_lt_max = ja < ja_max; \
450  \
451  octave_idx_type jb = m2.cidx (i); \
452  octave_idx_type jb_max = m2.cidx (i+1); \
453  bool jb_lt_max = jb < jb_max; \
454  \
455  while (ja_lt_max || jb_lt_max) \
456  { \
457  octave_quit (); \
458  if ((! jb_lt_max) || \
459  (ja_lt_max && (m1.ridx (ja) < m2.ridx (jb)))) \
460  { \
461  r.ridx (jx) = m1.ridx (ja); \
462  r.data (jx) = m1.data (ja) OP 0.; \
463  jx++; \
464  ja++; \
465  ja_lt_max= ja < ja_max; \
466  } \
467  else if ((! ja_lt_max) || \
468  (jb_lt_max && (m2.ridx (jb) < m1.ridx (ja)))) \
469  { \
470  r.ridx (jx) = m2.ridx (jb); \
471  r.data (jx) = 0. OP m2.data (jb); \
472  jx++; \
473  jb++; \
474  jb_lt_max= jb < jb_max; \
475  } \
476  else \
477  { \
478  if ((m1.data (ja) OP m2.data (jb)) != 0.) \
479  { \
480  r.data (jx) = m1.data (ja) OP m2.data (jb); \
481  r.ridx (jx) = m1.ridx (ja); \
482  jx++; \
483  } \
484  ja++; \
485  ja_lt_max= ja < ja_max; \
486  jb++; \
487  jb_lt_max= jb < jb_max; \
488  } \
489  } \
490  r.cidx (i+1) = jx; \
491  } \
492  \
493  r.maybe_compress (); \
494  } \
495  \
496  return r; \
497  }
498 
499 #define SPARSE_SMSM_BIN_OP_2(R, F, OP, M1, M2) \
500  R \
501  F (const M1& m1, const M2& m2) \
502  { \
503  R r; \
504  \
505  octave_idx_type m1_nr = m1.rows (); \
506  octave_idx_type m1_nc = m1.cols (); \
507  \
508  octave_idx_type m2_nr = m2.rows (); \
509  octave_idx_type m2_nc = m2.cols (); \
510  \
511  if (m1_nr == 1 && m1_nc == 1) \
512  { \
513  if (m1.elem (0,0) == 0.) \
514  r = R (m2_nr, m2_nc); \
515  else \
516  { \
517  r = R (m2); \
518  octave_idx_type m2_nnz = m2.nnz (); \
519  \
520  for (octave_idx_type i = 0 ; i < m2_nnz ; i++) \
521  { \
522  octave_quit (); \
523  r.data (i) = m1.data (0) OP r.data (i); \
524  } \
525  r.maybe_compress (); \
526  } \
527  } \
528  else if (m2_nr == 1 && m2_nc == 1) \
529  { \
530  if (m2.elem (0,0) == 0.) \
531  r = R (m1_nr, m1_nc); \
532  else \
533  { \
534  r = R (m1); \
535  octave_idx_type m1_nnz = m1.nnz (); \
536  \
537  for (octave_idx_type i = 0 ; i < m1_nnz ; i++) \
538  { \
539  octave_quit (); \
540  r.data (i) = r.data (i) OP m2.data (0); \
541  } \
542  r.maybe_compress (); \
543  } \
544  } \
545  else if (m1_nr != m2_nr || m1_nc != m2_nc) \
546  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
547  else \
548  { \
549  r = R (m1_nr, m1_nc, (m1.nnz () > m2.nnz () ? m1.nnz () : m2.nnz ())); \
550  \
551  octave_idx_type jx = 0; \
552  r.cidx (0) = 0; \
553  for (octave_idx_type i = 0 ; i < m1_nc ; i++) \
554  { \
555  octave_idx_type ja = m1.cidx (i); \
556  octave_idx_type ja_max = m1.cidx (i+1); \
557  bool ja_lt_max = ja < ja_max; \
558  \
559  octave_idx_type jb = m2.cidx (i); \
560  octave_idx_type jb_max = m2.cidx (i+1); \
561  bool jb_lt_max = jb < jb_max; \
562  \
563  while (ja_lt_max || jb_lt_max) \
564  { \
565  octave_quit (); \
566  if ((! jb_lt_max) || \
567  (ja_lt_max && (m1.ridx (ja) < m2.ridx (jb)))) \
568  { \
569  ja++; ja_lt_max= ja < ja_max; \
570  } \
571  else if ((! ja_lt_max) || \
572  (jb_lt_max && (m2.ridx (jb) < m1.ridx (ja)))) \
573  { \
574  jb++; jb_lt_max= jb < jb_max; \
575  } \
576  else \
577  { \
578  if ((m1.data (ja) OP m2.data (jb)) != 0.) \
579  { \
580  r.data (jx) = m1.data (ja) OP m2.data (jb); \
581  r.ridx (jx) = m1.ridx (ja); \
582  jx++; \
583  } \
584  ja++; ja_lt_max= ja < ja_max; \
585  jb++; jb_lt_max= jb < jb_max; \
586  } \
587  } \
588  r.cidx (i+1) = jx; \
589  } \
590  \
591  r.maybe_compress (); \
592  } \
593  \
594  return r; \
595  }
596 
597 #define SPARSE_SMSM_BIN_OP_3(R, F, OP, M1, M2) \
598  R \
599  F (const M1& m1, const M2& m2) \
600  { \
601  R r; \
602  \
603  octave_idx_type m1_nr = m1.rows (); \
604  octave_idx_type m1_nc = m1.cols (); \
605  \
606  octave_idx_type m2_nr = m2.rows (); \
607  octave_idx_type m2_nc = m2.cols (); \
608  \
609  if (m1_nr == 1 && m1_nc == 1) \
610  { \
611  if ((m1.elem (0,0) OP Complex ()) == Complex ()) \
612  { \
613  octave_idx_type m2_nnz = m2.nnz (); \
614  r = R (m2); \
615  for (octave_idx_type i = 0 ; i < m2_nnz ; i++) \
616  r.data (i) = m1.elem (0,0) OP r.data (i); \
617  r.maybe_compress (); \
618  } \
619  else \
620  { \
621  r = R (m2_nr, m2_nc, m1.elem (0,0) OP Complex ()); \
622  for (octave_idx_type j = 0 ; j < m2_nc ; j++) \
623  { \
624  octave_quit (); \
625  octave_idx_type idxj = j * m2_nr; \
626  for (octave_idx_type i = m2.cidx (j) ; i < m2.cidx (j+1) ; i++) \
627  { \
628  octave_quit (); \
629  r.data (idxj + m2.ridx (i)) = m1.elem (0,0) OP m2.data (i); \
630  } \
631  } \
632  r.maybe_compress (); \
633  } \
634  } \
635  else if (m2_nr == 1 && m2_nc == 1) \
636  { \
637  if ((Complex () OP m1.elem (0,0)) == Complex ()) \
638  { \
639  octave_idx_type m1_nnz = m1.nnz (); \
640  r = R (m1); \
641  for (octave_idx_type i = 0 ; i < m1_nnz ; i++) \
642  r.data (i) = r.data (i) OP m2.elem (0,0); \
643  r.maybe_compress (); \
644  } \
645  else \
646  { \
647  r = R (m1_nr, m1_nc, Complex () OP m2.elem (0,0)); \
648  for (octave_idx_type j = 0 ; j < m1_nc ; j++) \
649  { \
650  octave_quit (); \
651  octave_idx_type idxj = j * m1_nr; \
652  for (octave_idx_type i = m1.cidx (j) ; i < m1.cidx (j+1) ; i++) \
653  { \
654  octave_quit (); \
655  r.data (idxj + m1.ridx (i)) = m1.data (i) OP m2.elem (0,0); \
656  } \
657  } \
658  r.maybe_compress (); \
659  } \
660  } \
661  else if (m1_nr != m2_nr || m1_nc != m2_nc) \
662  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
663  else \
664  { \
665  \
666  /* FIXME: Kludge... Always double/Complex, so Complex () */ \
667  r = R (m1_nr, m1_nc, (Complex () OP Complex ())); \
668  \
669  for (octave_idx_type i = 0 ; i < m1_nc ; i++) \
670  { \
671  octave_idx_type ja = m1.cidx (i); \
672  octave_idx_type ja_max = m1.cidx (i+1); \
673  bool ja_lt_max = ja < ja_max; \
674  \
675  octave_idx_type jb = m2.cidx (i); \
676  octave_idx_type jb_max = m2.cidx (i+1); \
677  bool jb_lt_max = jb < jb_max; \
678  \
679  while (ja_lt_max || jb_lt_max) \
680  { \
681  octave_quit (); \
682  if ((! jb_lt_max) || \
683  (ja_lt_max && (m1.ridx (ja) < m2.ridx (jb)))) \
684  { \
685  /* keep those kludges coming */ \
686  r.elem (m1.ridx (ja),i) = m1.data (ja) OP Complex (); \
687  ja++; \
688  ja_lt_max= ja < ja_max; \
689  } \
690  else if ((! ja_lt_max) || \
691  (jb_lt_max && (m2.ridx (jb) < m1.ridx (ja)))) \
692  { \
693  /* keep those kludges coming */ \
694  r.elem (m2.ridx (jb),i) = Complex () OP m2.data (jb); \
695  jb++; \
696  jb_lt_max= jb < jb_max; \
697  } \
698  else \
699  { \
700  r.elem (m1.ridx (ja),i) = m1.data (ja) OP m2.data (jb); \
701  ja++; \
702  ja_lt_max= ja < ja_max; \
703  jb++; \
704  jb_lt_max= jb < jb_max; \
705  } \
706  } \
707  } \
708  r.maybe_compress (true); \
709  } \
710  \
711  return r; \
712  }
713 
714 // Note that SM ./ SM needs to take into account the NaN and Inf values
715 // implied by the division by zero.
716 // FIXME: Are the NaNs double(NaN) or Complex(NaN,Nan) in the complex case?
717 #define SPARSE_SMSM_BIN_OPS(R1, R2, M1, M2) \
718  SPARSE_SMSM_BIN_OP_1 (R1, operator +, +, M1, M2) \
719  SPARSE_SMSM_BIN_OP_1 (R1, operator -, -, M1, M2) \
720  SPARSE_SMSM_BIN_OP_2 (R2, product, *, M1, M2) \
721  SPARSE_SMSM_BIN_OP_3 (R2, quotient, /, M1, M2)
722 
723 // FIXME: this macro duplicates the bodies of the template functions
724 // defined in the SPARSE_SSM_CMP_OP and SPARSE_SMS_CMP_OP macros.
725 
726 #define SPARSE_SMSM_CMP_OP(F, OP, M1, M2) \
727  SparseBoolMatrix \
728  F (const M1& m1, const M2& m2) \
729  { \
730  SparseBoolMatrix r; \
731  \
732  octave_idx_type m1_nr = m1.rows (); \
733  octave_idx_type m1_nc = m1.cols (); \
734  \
735  octave_idx_type m2_nr = m2.rows (); \
736  octave_idx_type m2_nc = m2.cols (); \
737  \
738  M1::element_type Z1 = M1::element_type (); \
739  M2::element_type Z2 = M2::element_type (); \
740  \
741  if (m1_nr == 1 && m1_nc == 1) \
742  { \
743  if (m1.elem (0,0) OP Z2) \
744  { \
745  r = SparseBoolMatrix (m2_nr, m2_nc, true); \
746  for (octave_idx_type j = 0; j < m2_nc; j++) \
747  for (octave_idx_type i = m2.cidx (j); i < m2.cidx (j+1); i++) \
748  if (! (m1.elem (0,0) OP m2.data (i))) \
749  r.data (m2.ridx (i) + j * m2_nr) = false; \
750  r.maybe_compress (true); \
751  } \
752  else \
753  { \
754  r = SparseBoolMatrix (m2_nr, m2_nc, m2.nnz ()); \
755  r.cidx (0) = static_cast<octave_idx_type> (0); \
756  octave_idx_type nel = 0; \
757  for (octave_idx_type j = 0; j < m2_nc; j++) \
758  { \
759  for (octave_idx_type i = m2.cidx (j); i < m2.cidx (j+1); i++) \
760  if (m1.elem (0,0) OP m2.data (i)) \
761  { \
762  r.ridx (nel) = m2.ridx (i); \
763  r.data (nel++) = true; \
764  } \
765  r.cidx (j + 1) = nel; \
766  } \
767  r.maybe_compress (false); \
768  } \
769  } \
770  else if (m2_nr == 1 && m2_nc == 1) \
771  { \
772  if (Z1 OP m2.elem (0,0)) \
773  { \
774  r = SparseBoolMatrix (m1_nr, m1_nc, true); \
775  for (octave_idx_type j = 0; j < m1_nc; j++) \
776  for (octave_idx_type i = m1.cidx (j); i < m1.cidx (j+1); i++) \
777  if (! (m1.data (i) OP m2.elem (0,0))) \
778  r.data (m1.ridx (i) + j * m1_nr) = false; \
779  r.maybe_compress (true); \
780  } \
781  else \
782  { \
783  r = SparseBoolMatrix (m1_nr, m1_nc, m1.nnz ()); \
784  r.cidx (0) = static_cast<octave_idx_type> (0); \
785  octave_idx_type nel = 0; \
786  for (octave_idx_type j = 0; j < m1_nc; j++) \
787  { \
788  for (octave_idx_type i = m1.cidx (j); i < m1.cidx (j+1); i++) \
789  if (m1.data (i) OP m2.elem (0,0)) \
790  { \
791  r.ridx (nel) = m1.ridx (i); \
792  r.data (nel++) = true; \
793  } \
794  r.cidx (j + 1) = nel; \
795  } \
796  r.maybe_compress (false); \
797  } \
798  } \
799  else if (m1_nr == m2_nr && m1_nc == m2_nc) \
800  { \
801  if (m1_nr != 0 || m1_nc != 0) \
802  { \
803  if (Z1 OP Z2) \
804  { \
805  r = SparseBoolMatrix (m1_nr, m1_nc, true); \
806  for (octave_idx_type j = 0; j < m1_nc; j++) \
807  { \
808  octave_idx_type i1 = m1.cidx (j); \
809  octave_idx_type e1 = m1.cidx (j+1); \
810  octave_idx_type i2 = m2.cidx (j); \
811  octave_idx_type e2 = m2.cidx (j+1); \
812  while (i1 < e1 || i2 < e2) \
813  { \
814  if (i1 == e1 || (i2 < e2 && m1.ridx (i1) > m2.ridx (i2))) \
815  { \
816  if (! (Z1 OP m2.data (i2))) \
817  r.data (m2.ridx (i2) + j * m1_nr) = false; \
818  i2++; \
819  } \
820  else if (i2 == e2 || m1.ridx (i1) < m2.ridx (i2)) \
821  { \
822  if (! (m1.data (i1) OP Z2)) \
823  r.data (m1.ridx (i1) + j * m1_nr) = false; \
824  i1++; \
825  } \
826  else \
827  { \
828  if (! (m1.data (i1) OP m2.data (i2))) \
829  r.data (m1.ridx (i1) + j * m1_nr) = false; \
830  i1++; \
831  i2++; \
832  } \
833  } \
834  } \
835  r.maybe_compress (true); \
836  } \
837  else \
838  { \
839  r = SparseBoolMatrix (m1_nr, m1_nc, m1.nnz () + m2.nnz ()); \
840  r.cidx (0) = static_cast<octave_idx_type> (0); \
841  octave_idx_type nel = 0; \
842  for (octave_idx_type j = 0; j < m1_nc; j++) \
843  { \
844  octave_idx_type i1 = m1.cidx (j); \
845  octave_idx_type e1 = m1.cidx (j+1); \
846  octave_idx_type i2 = m2.cidx (j); \
847  octave_idx_type e2 = m2.cidx (j+1); \
848  while (i1 < e1 || i2 < e2) \
849  { \
850  if (i1 == e1 || (i2 < e2 && m1.ridx (i1) > m2.ridx (i2))) \
851  { \
852  if (Z1 OP m2.data (i2)) \
853  { \
854  r.ridx (nel) = m2.ridx (i2); \
855  r.data (nel++) = true; \
856  } \
857  i2++; \
858  } \
859  else if (i2 == e2 || m1.ridx (i1) < m2.ridx (i2)) \
860  { \
861  if (m1.data (i1) OP Z2) \
862  { \
863  r.ridx (nel) = m1.ridx (i1); \
864  r.data (nel++) = true; \
865  } \
866  i1++; \
867  } \
868  else \
869  { \
870  if (m1.data (i1) OP m2.data (i2)) \
871  { \
872  r.ridx (nel) = m1.ridx (i1); \
873  r.data (nel++) = true; \
874  } \
875  i1++; \
876  i2++; \
877  } \
878  } \
879  r.cidx (j + 1) = nel; \
880  } \
881  r.maybe_compress (false); \
882  } \
883  } \
884  } \
885  else \
886  { \
887  if ((m1_nr != 0 || m1_nc != 0) && (m2_nr != 0 || m2_nc != 0)) \
888  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
889  } \
890  return r; \
891  }
892 
893 #define SPARSE_SMSM_CMP_OPS(M1, M2) \
894  SPARSE_SMSM_CMP_OP (mx_el_lt, <, M1, M2) \
895  SPARSE_SMSM_CMP_OP (mx_el_le, <=, M1, M2) \
896  SPARSE_SMSM_CMP_OP (mx_el_ge, >=, M1, M2) \
897  SPARSE_SMSM_CMP_OP (mx_el_gt, >, M1, M2) \
898  SPARSE_SMSM_CMP_OP (mx_el_eq, ==, M1, M2) \
899  SPARSE_SMSM_CMP_OP (mx_el_ne, !=, M1, M2)
900 
901 #define SPARSE_SMSM_EQNE_OPS(M1, M2) \
902  SPARSE_SMSM_CMP_OP (mx_el_eq, ==, M1, M2) \
903  SPARSE_SMSM_CMP_OP (mx_el_ne, !=, M1, M2)
904 
905 #define SPARSE_SMSM_BOOL_AND_OP(M1, M2) \
906  extern SparseBoolMatrix mx_el_and (const M1&, const M2::element_type&); \
907  extern SparseBoolMatrix mx_el_and (const M1::element_type&, const M2&); \
908  SparseBoolMatrix \
909  mx_el_and (const M1& m1, const M2& m2) \
910  { \
911  SparseBoolMatrix r; \
912  \
913  octave_idx_type m1_nr = m1.rows (); \
914  octave_idx_type m1_nc = m1.cols (); \
915  \
916  octave_idx_type m2_nr = m2.rows (); \
917  octave_idx_type m2_nc = m2.cols (); \
918  \
919  M1::element_type lhs_zero = M1::element_type (); \
920  M2::element_type rhs_zero = M2::element_type (); \
921  \
922  if (m1_nr == 1 && m1_nc == 1) \
923  return mx_el_and (m1.elem (0,0), m2); \
924  else if (m2_nr == 1 && m2_nc == 1) \
925  return mx_el_and (m1, m2.elem (0,0)); \
926  else if (m1_nr == m2_nr && m1_nc == m2_nc) \
927  { \
928  if (m1_nr != 0 || m1_nc != 0) \
929  { \
930  r = SparseBoolMatrix (m1_nr, m1_nc, m1.nnz () + m2.nnz ()); \
931  r.cidx (0) = static_cast<octave_idx_type> (0); \
932  octave_idx_type nel = 0; \
933  for (octave_idx_type j = 0; j < m1_nc; j++) \
934  { \
935  octave_idx_type i1 = m1.cidx (j); \
936  octave_idx_type e1 = m1.cidx (j+1); \
937  octave_idx_type i2 = m2.cidx (j); \
938  octave_idx_type e2 = m2.cidx (j+1); \
939  while (i1 < e1 || i2 < e2) \
940  { \
941  if (i1 == e1 || (i2 < e2 && m1.ridx (i1) > m2.ridx (i2))) \
942  i2++; \
943  else if (i2 == e2 || m1.ridx (i1) < m2.ridx (i2)) \
944  i1++; \
945  else \
946  { \
947  if (m1.data (i1) != lhs_zero && m2.data (i2) != rhs_zero) \
948  { \
949  r.ridx (nel) = m1.ridx (i1); \
950  r.data (nel++) = true; \
951  } \
952  i1++; \
953  i2++; \
954  } \
955  } \
956  r.cidx (j + 1) = nel; \
957  } \
958  r.maybe_compress (false); \
959  } \
960  } \
961  else \
962  { \
963  if ((m1_nr != 0 || m1_nc != 0) && (m2_nr != 0 || m2_nc != 0)) \
964  octave::err_nonconformant ("mx_el_and_", m1_nr, m1_nc, m2_nr, m2_nc); \
965  } \
966  return r; \
967  }
968 
969 #define SPARSE_SMSM_BOOL_OR_OP(M1, M2) \
970  extern SparseBoolMatrix mx_el_or (const M1&, const M2::element_type&); \
971  extern SparseBoolMatrix mx_el_or (const M1::element_type&, const M2&); \
972  SparseBoolMatrix \
973  mx_el_or (const M1& m1, const M2& m2) \
974  { \
975  SparseBoolMatrix r; \
976  \
977  octave_idx_type m1_nr = m1.rows (); \
978  octave_idx_type m1_nc = m1.cols (); \
979  \
980  octave_idx_type m2_nr = m2.rows (); \
981  octave_idx_type m2_nc = m2.cols (); \
982  \
983  M1::element_type lhs_zero = M1::element_type (); \
984  M2::element_type rhs_zero = M2::element_type (); \
985  \
986  if (m1_nr == 1 && m1_nc == 1) \
987  return mx_el_or (m1.elem (0,0), m2); \
988  else if (m2_nr == 1 && m2_nc == 1) \
989  return mx_el_or (m1, m2.elem (0,0)); \
990  else if (m1_nr == m2_nr && m1_nc == m2_nc) \
991  { \
992  if (m1_nr != 0 || m1_nc != 0) \
993  { \
994  r = SparseBoolMatrix (m1_nr, m1_nc, m1.nnz () + m2.nnz ()); \
995  r.cidx (0) = static_cast<octave_idx_type> (0); \
996  octave_idx_type nel = 0; \
997  for (octave_idx_type j = 0; j < m1_nc; j++) \
998  { \
999  octave_idx_type i1 = m1.cidx (j); \
1000  octave_idx_type e1 = m1.cidx (j+1); \
1001  octave_idx_type i2 = m2.cidx (j); \
1002  octave_idx_type e2 = m2.cidx (j+1); \
1003  while (i1 < e1 || i2 < e2) \
1004  { \
1005  if (i1 == e1 || (i2 < e2 && m1.ridx (i1) > m2.ridx (i2))) \
1006  { \
1007  if (m2.data (i2) != rhs_zero) \
1008  { \
1009  r.ridx (nel) = m2.ridx (i2); \
1010  r.data (nel++) = true; \
1011  } \
1012  i2++; \
1013  } \
1014  else if (i2 == e2 || m1.ridx (i1) < m2.ridx (i2)) \
1015  { \
1016  if (m1.data (i1) != lhs_zero) \
1017  { \
1018  r.ridx (nel) = m1.ridx (i1); \
1019  r.data (nel++) = true; \
1020  } \
1021  i1++; \
1022  } \
1023  else \
1024  { \
1025  if (m1.data (i1) != lhs_zero || m2.data (i2) != rhs_zero) \
1026  { \
1027  r.ridx (nel) = m1.ridx (i1); \
1028  r.data (nel++) = true; \
1029  } \
1030  i1++; \
1031  i2++; \
1032  } \
1033  } \
1034  r.cidx (j + 1) = nel; \
1035  } \
1036  r.maybe_compress (false); \
1037  } \
1038  } \
1039  else \
1040  { \
1041  if ((m1_nr != 0 || m1_nc != 0) && (m2_nr != 0 || m2_nc != 0)) \
1042  octave::err_nonconformant ("mx_el_or", m1_nr, m1_nc, m2_nr, m2_nc); \
1043  } \
1044  return r; \
1045  }
1046 
1047 #define SPARSE_SMSM_BOOL_OPS(M1, M2) \
1048  SPARSE_SMSM_BOOL_AND_OP (M1, M2) \
1049  SPARSE_SMSM_BOOL_OR_OP (M1, M2)
1050 
1051 // matrix by sparse matrix operations.
1052 
1053 #define SPARSE_MSM_BIN_OP_1(R, F, OP, M1, M2) \
1054  R \
1055  F (const M1& m1, const M2& m2) \
1056  { \
1057  R r; \
1058  \
1059  octave_idx_type m1_nr = m1.rows (); \
1060  octave_idx_type m1_nc = m1.cols (); \
1061  \
1062  octave_idx_type m2_nr = m2.rows (); \
1063  octave_idx_type m2_nc = m2.cols (); \
1064  \
1065  if (m2_nr == 1 && m2_nc == 1) \
1066  r = R (m1 OP m2.elem (0,0)); \
1067  else if (m1_nr != m2_nr || m1_nc != m2_nc) \
1068  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
1069  else \
1070  { \
1071  r = R (F (m1, m2.matrix_value ())); \
1072  } \
1073  return r; \
1074  }
1075 
1076 #define SPARSE_MSM_BIN_OP_2(R, F, OP, M1, M2) \
1077  R \
1078  F (const M1& m1, const M2& m2) \
1079  { \
1080  R r; \
1081  \
1082  octave_idx_type m1_nr = m1.rows (); \
1083  octave_idx_type m1_nc = m1.cols (); \
1084  \
1085  octave_idx_type m2_nr = m2.rows (); \
1086  octave_idx_type m2_nc = m2.cols (); \
1087  \
1088  if (m2_nr == 1 && m2_nc == 1) \
1089  r = R (m1 OP m2.elem (0,0)); \
1090  else if (m1_nr != m2_nr || m1_nc != m2_nc) \
1091  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
1092  else \
1093  { \
1094  if (do_mx_check (m1, mx_inline_all_finite<M1::element_type>)) \
1095  { \
1096  /* Sparsity pattern is preserved. */ \
1097  octave_idx_type m2_nz = m2.nnz (); \
1098  r = R (m2_nr, m2_nc, m2_nz); \
1099  for (octave_idx_type j = 0, k = 0; j < m2_nc; j++) \
1100  { \
1101  octave_quit (); \
1102  for (octave_idx_type i = m2.cidx (j); i < m2.cidx (j+1); i++) \
1103  { \
1104  octave_idx_type mri = m2.ridx (i); \
1105  R::element_type x = m1(mri, j) OP m2.data (i); \
1106  if (x != 0.0) \
1107  { \
1108  r.xdata (k) = x; \
1109  r.xridx (k) = m2.ridx (i); \
1110  k++; \
1111  } \
1112  } \
1113  r.xcidx (j+1) = k; \
1114  } \
1115  r.maybe_compress (false); \
1116  return r; \
1117  } \
1118  else \
1119  r = R (F (m1, m2.matrix_value ())); \
1120  } \
1121  \
1122  return r; \
1123  }
1124 
1125 #define SPARSE_MSM_BIN_OPS(R1, R2, M1, M2) \
1126  SPARSE_MSM_BIN_OP_1 (R1, operator +, +, M1, M2) \
1127  SPARSE_MSM_BIN_OP_1 (R1, operator -, -, M1, M2) \
1128  SPARSE_MSM_BIN_OP_2 (R2, product, *, M1, M2) \
1129  SPARSE_MSM_BIN_OP_1 (R2, quotient, /, M1, M2)
1130 
1131 #define SPARSE_MSM_CMP_OP(F, OP, M1, M2) \
1132  SparseBoolMatrix \
1133  F (const M1& m1, const M2& m2) \
1134  { \
1135  SparseBoolMatrix r; \
1136  \
1137  octave_idx_type m1_nr = m1.rows (); \
1138  octave_idx_type m1_nc = m1.cols (); \
1139  \
1140  octave_idx_type m2_nr = m2.rows (); \
1141  octave_idx_type m2_nc = m2.cols (); \
1142  \
1143  if (m2_nr == 1 && m2_nc == 1) \
1144  r = SparseBoolMatrix (F (m1, m2.elem (0,0))); \
1145  else if (m1_nr == m2_nr && m1_nc == m2_nc) \
1146  { \
1147  if (m1_nr != 0 || m1_nc != 0) \
1148  { \
1149  /* Count num of nonzero elements */ \
1150  octave_idx_type nel = 0; \
1151  for (octave_idx_type j = 0; j < m1_nc; j++) \
1152  for (octave_idx_type i = 0; i < m1_nr; i++) \
1153  if (m1.elem (i, j) OP m2.elem (i, j)) \
1154  nel++; \
1155  \
1156  r = SparseBoolMatrix (m1_nr, m1_nc, nel); \
1157  \
1158  octave_idx_type ii = 0; \
1159  r.cidx (0) = 0; \
1160  for (octave_idx_type j = 0; j < m1_nc; j++) \
1161  { \
1162  for (octave_idx_type i = 0; i < m1_nr; i++) \
1163  { \
1164  bool el = m1.elem (i, j) OP m2.elem (i, j); \
1165  if (el) \
1166  { \
1167  r.data (ii) = el; \
1168  r.ridx (ii++) = i; \
1169  } \
1170  } \
1171  r.cidx (j+1) = ii; \
1172  } \
1173  } \
1174  } \
1175  else \
1176  { \
1177  if ((m1_nr != 0 || m1_nc != 0) && (m2_nr != 0 || m2_nc != 0)) \
1178  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
1179  } \
1180  return r; \
1181  }
1182 
1183 #define SPARSE_MSM_CMP_OPS(M1, M2) \
1184  SPARSE_MSM_CMP_OP (mx_el_lt, <, M1, M2) \
1185  SPARSE_MSM_CMP_OP (mx_el_le, <=, M1, M2) \
1186  SPARSE_MSM_CMP_OP (mx_el_ge, >=, M1, M2) \
1187  SPARSE_MSM_CMP_OP (mx_el_gt, >, M1, M2) \
1188  SPARSE_MSM_CMP_OP (mx_el_eq, ==, M1, M2) \
1189  SPARSE_MSM_CMP_OP (mx_el_ne, !=, M1, M2)
1190 
1191 #define SPARSE_MSM_EQNE_OPS(M1, M2) \
1192  SPARSE_MSM_CMP_OP (mx_el_eq, ==, M1, M2) \
1193  SPARSE_MSM_CMP_OP (mx_el_ne, !=, M1, M2)
1194 
1195 #define SPARSE_MSM_BOOL_OP(F, OP, M1, M2) \
1196  SparseBoolMatrix \
1197  F (const M1& m1, const M2& m2) \
1198  { \
1199  SparseBoolMatrix r; \
1200  \
1201  octave_idx_type m1_nr = m1.rows (); \
1202  octave_idx_type m1_nc = m1.cols (); \
1203  \
1204  octave_idx_type m2_nr = m2.rows (); \
1205  octave_idx_type m2_nc = m2.cols (); \
1206  \
1207  M1::element_type lhs_zero = M1::element_type (); \
1208  M2::element_type rhs_zero = M2::element_type (); \
1209  \
1210  if (m2_nr == 1 && m2_nc == 1) \
1211  r = SparseBoolMatrix (F (m1, m2.elem (0,0))); \
1212  else if (m1_nr == m2_nr && m1_nc == m2_nc) \
1213  { \
1214  if (m1_nr != 0 || m1_nc != 0) \
1215  { \
1216  /* Count num of nonzero elements */ \
1217  octave_idx_type nel = 0; \
1218  for (octave_idx_type j = 0; j < m1_nc; j++) \
1219  for (octave_idx_type i = 0; i < m1_nr; i++) \
1220  if ((m1.elem (i, j) != lhs_zero) \
1221  OP (m2.elem (i, j) != rhs_zero)) \
1222  nel++; \
1223  \
1224  r = SparseBoolMatrix (m1_nr, m1_nc, nel); \
1225  \
1226  octave_idx_type ii = 0; \
1227  r.cidx (0) = 0; \
1228  for (octave_idx_type j = 0; j < m1_nc; j++) \
1229  { \
1230  for (octave_idx_type i = 0; i < m1_nr; i++) \
1231  { \
1232  bool el = (m1.elem (i, j) != lhs_zero) \
1233  OP (m2.elem (i, j) != rhs_zero); \
1234  if (el) \
1235  { \
1236  r.data (ii) = el; \
1237  r.ridx (ii++) = i; \
1238  } \
1239  } \
1240  r.cidx (j+1) = ii; \
1241  } \
1242  } \
1243  } \
1244  else \
1245  { \
1246  if ((m1_nr != 0 || m1_nc != 0) && (m2_nr != 0 || m2_nc != 0)) \
1247  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
1248  } \
1249  return r; \
1250  }
1251 
1252 #define SPARSE_MSM_BOOL_OPS(M1, M2) \
1253  SPARSE_MSM_BOOL_OP (mx_el_and, &&, M1, M2) \
1254  SPARSE_MSM_BOOL_OP (mx_el_or, ||, M1, M2)
1255 
1256 // sparse matrix by matrix operations.
1257 
1258 #define SPARSE_SMM_BIN_OP_1(R, F, OP, M1, M2) \
1259  R \
1260  F (const M1& m1, const M2& m2) \
1261  { \
1262  R r; \
1263  \
1264  octave_idx_type m1_nr = m1.rows (); \
1265  octave_idx_type m1_nc = m1.cols (); \
1266  \
1267  octave_idx_type m2_nr = m2.rows (); \
1268  octave_idx_type m2_nc = m2.cols (); \
1269  \
1270  if (m1_nr == 1 && m1_nc == 1) \
1271  r = R (m1.elem (0,0) OP m2); \
1272  else if (m1_nr != m2_nr || m1_nc != m2_nc) \
1273  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
1274  else \
1275  { \
1276  r = R (m1.matrix_value () OP m2); \
1277  } \
1278  return r; \
1279  }
1280 
1281 // sm .* m preserves sparsity if m contains no Infs nor Nans.
1282 #define SPARSE_SMM_BIN_OP_2_CHECK_product(ET) \
1283  do_mx_check (m2, mx_inline_all_finite<ET>)
1284 
1285 // sm ./ m preserves sparsity if m contains no NaNs or zeros.
1286 #define SPARSE_SMM_BIN_OP_2_CHECK_quotient(ET) \
1287  ! do_mx_check (m2, mx_inline_any_nan<ET>) && m2.nnz () == m2.numel ()
1288 
1289 #define SPARSE_SMM_BIN_OP_2(R, F, OP, M1, M2) \
1290  R \
1291  F (const M1& m1, const M2& m2) \
1292  { \
1293  R r; \
1294  \
1295  octave_idx_type m1_nr = m1.rows (); \
1296  octave_idx_type m1_nc = m1.cols (); \
1297  \
1298  octave_idx_type m2_nr = m2.rows (); \
1299  octave_idx_type m2_nc = m2.cols (); \
1300  \
1301  if (m1_nr == 1 && m1_nc == 1) \
1302  r = R (m1.elem (0,0) OP m2); \
1303  else if (m1_nr != m2_nr || m1_nc != m2_nc) \
1304  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
1305  else \
1306  { \
1307  if (SPARSE_SMM_BIN_OP_2_CHECK_ ## F(M2::element_type)) \
1308  { \
1309  /* Sparsity pattern is preserved. */ \
1310  octave_idx_type m1_nz = m1.nnz (); \
1311  r = R (m1_nr, m1_nc, m1_nz); \
1312  for (octave_idx_type j = 0, k = 0; j < m1_nc; j++) \
1313  { \
1314  octave_quit (); \
1315  for (octave_idx_type i = m1.cidx (j); i < m1.cidx (j+1); i++) \
1316  { \
1317  octave_idx_type mri = m1.ridx (i); \
1318  R::element_type x = m1.data (i) OP m2 (mri, j); \
1319  if (x != 0.0) \
1320  { \
1321  r.xdata (k) = x; \
1322  r.xridx (k) = m1.ridx (i); \
1323  k++; \
1324  } \
1325  } \
1326  r.xcidx (j+1) = k; \
1327  } \
1328  r.maybe_compress (false); \
1329  return r; \
1330  } \
1331  else \
1332  r = R (F (m1.matrix_value (), m2)); \
1333  } \
1334  \
1335  return r; \
1336  }
1337 
1338 #define SPARSE_SMM_BIN_OPS(R1, R2, M1, M2) \
1339  SPARSE_SMM_BIN_OP_1 (R1, operator +, +, M1, M2) \
1340  SPARSE_SMM_BIN_OP_1 (R1, operator -, -, M1, M2) \
1341  SPARSE_SMM_BIN_OP_2 (R2, product, *, M1, M2) \
1342  SPARSE_SMM_BIN_OP_2 (R2, quotient, /, M1, M2)
1343 
1344 #define SPARSE_SMM_CMP_OP(F, OP, M1, M2) \
1345  SparseBoolMatrix \
1346  F (const M1& m1, const M2& m2) \
1347  { \
1348  SparseBoolMatrix r; \
1349  \
1350  octave_idx_type m1_nr = m1.rows (); \
1351  octave_idx_type m1_nc = m1.cols (); \
1352  \
1353  octave_idx_type m2_nr = m2.rows (); \
1354  octave_idx_type m2_nc = m2.cols (); \
1355  \
1356  if (m1_nr == 1 && m1_nc == 1) \
1357  r = SparseBoolMatrix (F (m1.elem (0,0), m2)); \
1358  else if (m1_nr == m2_nr && m1_nc == m2_nc) \
1359  { \
1360  if (m1_nr != 0 || m1_nc != 0) \
1361  { \
1362  /* Count num of nonzero elements */ \
1363  octave_idx_type nel = 0; \
1364  for (octave_idx_type j = 0; j < m1_nc; j++) \
1365  for (octave_idx_type i = 0; i < m1_nr; i++) \
1366  if (m1.elem (i, j) OP m2.elem (i, j)) \
1367  nel++; \
1368  \
1369  r = SparseBoolMatrix (m1_nr, m1_nc, nel); \
1370  \
1371  octave_idx_type ii = 0; \
1372  r.cidx (0) = 0; \
1373  for (octave_idx_type j = 0; j < m1_nc; j++) \
1374  { \
1375  for (octave_idx_type i = 0; i < m1_nr; i++) \
1376  { \
1377  bool el = m1.elem (i, j) OP m2.elem (i, j); \
1378  if (el) \
1379  { \
1380  r.data (ii) = el; \
1381  r.ridx (ii++) = i; \
1382  } \
1383  } \
1384  r.cidx (j+1) = ii; \
1385  } \
1386  } \
1387  } \
1388  else \
1389  { \
1390  if ((m1_nr != 0 || m1_nc != 0) && (m2_nr != 0 || m2_nc != 0)) \
1391  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
1392  } \
1393  return r; \
1394  }
1395 
1396 #define SPARSE_SMM_CMP_OPS(M1, M2) \
1397  SPARSE_SMM_CMP_OP (mx_el_lt, <, M1, M2) \
1398  SPARSE_SMM_CMP_OP (mx_el_le, <=, M1, M2) \
1399  SPARSE_SMM_CMP_OP (mx_el_ge, >=, M1, M2) \
1400  SPARSE_SMM_CMP_OP (mx_el_gt, >, M1, M2) \
1401  SPARSE_SMM_CMP_OP (mx_el_eq, ==, M1, M2) \
1402  SPARSE_SMM_CMP_OP (mx_el_ne, !=, M1, M2)
1403 
1404 #define SPARSE_SMM_EQNE_OPS(M1, M2) \
1405  SPARSE_SMM_CMP_OP (mx_el_eq, ==, M1, M2) \
1406  SPARSE_SMM_CMP_OP (mx_el_ne, !=, M1, M2)
1407 
1408 #define SPARSE_SMM_BOOL_OP(F, OP, M1, M2) \
1409  SparseBoolMatrix \
1410  F (const M1& m1, const M2& m2) \
1411  { \
1412  SparseBoolMatrix r; \
1413  \
1414  octave_idx_type m1_nr = m1.rows (); \
1415  octave_idx_type m1_nc = m1.cols (); \
1416  \
1417  octave_idx_type m2_nr = m2.rows (); \
1418  octave_idx_type m2_nc = m2.cols (); \
1419  \
1420  M1::element_type lhs_zero = M1::element_type (); \
1421  M2::element_type rhs_zero = M2::element_type (); \
1422  \
1423  if (m1_nr == 1 && m1_nc == 1) \
1424  r = SparseBoolMatrix (F (m1.elem (0,0), m2)); \
1425  else if (m1_nr == m2_nr && m1_nc == m2_nc) \
1426  { \
1427  if (m1_nr != 0 || m1_nc != 0) \
1428  { \
1429  /* Count num of nonzero elements */ \
1430  octave_idx_type nel = 0; \
1431  for (octave_idx_type j = 0; j < m1_nc; j++) \
1432  for (octave_idx_type i = 0; i < m1_nr; i++) \
1433  if ((m1.elem (i, j) != lhs_zero) \
1434  OP (m2.elem (i, j) != rhs_zero)) \
1435  nel++; \
1436  \
1437  r = SparseBoolMatrix (m1_nr, m1_nc, nel); \
1438  \
1439  octave_idx_type ii = 0; \
1440  r.cidx (0) = 0; \
1441  for (octave_idx_type j = 0; j < m1_nc; j++) \
1442  { \
1443  for (octave_idx_type i = 0; i < m1_nr; i++) \
1444  { \
1445  bool el = (m1.elem (i, j) != lhs_zero) \
1446  OP (m2.elem (i, j) != rhs_zero); \
1447  if (el) \
1448  { \
1449  r.data (ii) = el; \
1450  r.ridx (ii++) = i; \
1451  } \
1452  } \
1453  r.cidx (j+1) = ii; \
1454  } \
1455  } \
1456  } \
1457  else \
1458  { \
1459  if ((m1_nr != 0 || m1_nc != 0) && (m2_nr != 0 || m2_nc != 0)) \
1460  octave::err_nonconformant (#F, m1_nr, m1_nc, m2_nr, m2_nc); \
1461  } \
1462  return r; \
1463  }
1464 
1465 #define SPARSE_SMM_BOOL_OPS(M1, M2) \
1466  SPARSE_SMM_BOOL_OP (mx_el_and, &&, M1, M2) \
1467  SPARSE_SMM_BOOL_OP (mx_el_or, ||, M1, M2)
1468 
1469 // Avoid some code duplication. Maybe we should use templates.
1470 
1471 #define SPARSE_CUMSUM(RET_TYPE, ELT_TYPE, FCN) \
1472  \
1473  octave_idx_type nr = rows (); \
1474  octave_idx_type nc = cols (); \
1475  \
1476  RET_TYPE retval; \
1477  \
1478  if (nr > 0 && nc > 0) \
1479  { \
1480  if ((nr == 1 && dim == -1) || dim == 1) \
1481  /* Ugly!! Is there a better way? */ \
1482  retval = transpose (). FCN (0) .transpose (); \
1483  else \
1484  { \
1485  octave_idx_type nel = 0; \
1486  for (octave_idx_type i = 0; i < nc; i++) \
1487  { \
1488  ELT_TYPE t = ELT_TYPE (); \
1489  for (octave_idx_type j = cidx (i); j < cidx (i+1); j++) \
1490  { \
1491  t += data (j); \
1492  if (t != ELT_TYPE ()) \
1493  { \
1494  if (j == cidx (i+1) - 1) \
1495  nel += nr - ridx (j); \
1496  else \
1497  nel += ridx (j+1) - ridx (j); \
1498  } \
1499  } \
1500  } \
1501  retval = RET_TYPE (nr, nc, nel); \
1502  retval.cidx (0) = 0; \
1503  octave_idx_type ii = 0; \
1504  for (octave_idx_type i = 0; i < nc; i++) \
1505  { \
1506  ELT_TYPE t = ELT_TYPE (); \
1507  for (octave_idx_type j = cidx (i); j < cidx (i+1); j++) \
1508  { \
1509  t += data (j); \
1510  if (t != ELT_TYPE ()) \
1511  { \
1512  if (j == cidx (i+1) - 1) \
1513  { \
1514  for (octave_idx_type k = ridx (j); k < nr; k++) \
1515  { \
1516  retval.data (ii) = t; \
1517  retval.ridx (ii++) = k; \
1518  } \
1519  } \
1520  else \
1521  { \
1522  for (octave_idx_type k = ridx (j); k < ridx (j+1); k++) \
1523  { \
1524  retval.data (ii) = t; \
1525  retval.ridx (ii++) = k; \
1526  } \
1527  } \
1528  } \
1529  } \
1530  retval.cidx (i+1) = ii; \
1531  } \
1532  } \
1533  } \
1534  else \
1535  retval = RET_TYPE (nr,nc); \
1536  \
1537  return retval
1538 
1539 #define SPARSE_CUMPROD(RET_TYPE, ELT_TYPE, FCN) \
1540  \
1541  octave_idx_type nr = rows (); \
1542  octave_idx_type nc = cols (); \
1543  \
1544  RET_TYPE retval; \
1545  \
1546  if (nr > 0 && nc > 0) \
1547  { \
1548  if ((nr == 1 && dim == -1) || dim == 1) \
1549  /* Ugly!! Is there a better way? */ \
1550  retval = transpose (). FCN (0) .transpose (); \
1551  else \
1552  { \
1553  octave_idx_type nel = 0; \
1554  for (octave_idx_type i = 0; i < nc; i++) \
1555  { \
1556  octave_idx_type jj = 0; \
1557  for (octave_idx_type j = cidx (i); j < cidx (i+1); j++) \
1558  { \
1559  if (jj == ridx (j)) \
1560  { \
1561  nel++; \
1562  jj++; \
1563  } \
1564  else \
1565  break; \
1566  } \
1567  } \
1568  retval = RET_TYPE (nr, nc, nel); \
1569  retval.cidx (0) = 0; \
1570  octave_idx_type ii = 0; \
1571  for (octave_idx_type i = 0; i < nc; i++) \
1572  { \
1573  ELT_TYPE t = ELT_TYPE (1.); \
1574  octave_idx_type jj = 0; \
1575  for (octave_idx_type j = cidx (i); j < cidx (i+1); j++) \
1576  { \
1577  if (jj == ridx (j)) \
1578  { \
1579  t *= data (j); \
1580  retval.data (ii) = t; \
1581  retval.ridx (ii++) = jj++; \
1582  } \
1583  else \
1584  break; \
1585  } \
1586  retval.cidx (i+1) = ii; \
1587  } \
1588  } \
1589  } \
1590  else \
1591  retval = RET_TYPE (nr,nc); \
1592  \
1593  return retval
1594 
1595 #define SPARSE_BASE_REDUCTION_OP(RET_TYPE, EL_TYPE, ROW_EXPR, COL_EXPR, \
1596  INIT_VAL, MT_RESULT) \
1597  \
1598  octave_idx_type nr = rows (); \
1599  octave_idx_type nc = cols (); \
1600  \
1601  RET_TYPE retval; \
1602  \
1603  if (nr > 0 && nc > 0) \
1604  { \
1605  if ((nr == 1 && dim == -1) || dim == 1) \
1606  { \
1607  /* Define j here to allow fancy definition for prod method */ \
1608  octave_idx_type j = 0; \
1609  OCTAVE_LOCAL_BUFFER (EL_TYPE, tmp, nr); \
1610  \
1611  for (octave_idx_type i = 0; i < nr; i++) \
1612  tmp[i] = INIT_VAL; \
1613  for (j = 0; j < nc; j++) \
1614  { \
1615  for (octave_idx_type i = cidx (j); i < cidx (j + 1); i++) \
1616  { \
1617  ROW_EXPR; \
1618  } \
1619  } \
1620  octave_idx_type nel = 0; \
1621  for (octave_idx_type i = 0; i < nr; i++) \
1622  if (tmp[i] != EL_TYPE ()) \
1623  nel++; \
1624  retval = RET_TYPE (nr, static_cast<octave_idx_type> (1), nel); \
1625  retval.cidx (0) = 0; \
1626  retval.cidx (1) = nel; \
1627  nel = 0; \
1628  for (octave_idx_type i = 0; i < nr; i++) \
1629  if (tmp[i] != EL_TYPE ()) \
1630  { \
1631  retval.data (nel) = tmp[i]; \
1632  retval.ridx (nel++) = i; \
1633  } \
1634  } \
1635  else \
1636  { \
1637  OCTAVE_LOCAL_BUFFER (EL_TYPE, tmp, nc); \
1638  \
1639  for (octave_idx_type j = 0; j < nc; j++) \
1640  { \
1641  tmp[j] = INIT_VAL; \
1642  for (octave_idx_type i = cidx (j); i < cidx (j + 1); i++) \
1643  { \
1644  COL_EXPR; \
1645  } \
1646  } \
1647  octave_idx_type nel = 0; \
1648  for (octave_idx_type i = 0; i < nc; i++) \
1649  if (tmp[i] != EL_TYPE ()) \
1650  nel++; \
1651  retval = RET_TYPE (static_cast<octave_idx_type> (1), nc, nel); \
1652  retval.cidx (0) = 0; \
1653  nel = 0; \
1654  for (octave_idx_type i = 0; i < nc; i++) \
1655  if (tmp[i] != EL_TYPE ()) \
1656  { \
1657  retval.data (nel) = tmp[i]; \
1658  retval.ridx (nel++) = 0; \
1659  retval.cidx (i+1) = retval.cidx (i) + 1; \
1660  } \
1661  else \
1662  retval.cidx (i+1) = retval.cidx (i); \
1663  } \
1664  } \
1665  else if (nc == 0 && (nr == 0 || (nr == 1 && dim == -1))) \
1666  { \
1667  if (MT_RESULT) \
1668  { \
1669  retval = RET_TYPE (static_cast<octave_idx_type> (1), \
1670  static_cast<octave_idx_type> (1), \
1671  static_cast<octave_idx_type> (1)); \
1672  retval.cidx (0) = 0; \
1673  retval.cidx (1) = 1; \
1674  retval.ridx (0) = 0; \
1675  retval.data (0) = MT_RESULT; \
1676  } \
1677  else \
1678  retval = RET_TYPE (static_cast<octave_idx_type> (1), \
1679  static_cast<octave_idx_type> (1), \
1680  static_cast<octave_idx_type> (0)); \
1681  } \
1682  else if (nr == 0 && (dim == 0 || dim == -1)) \
1683  { \
1684  if (MT_RESULT) \
1685  { \
1686  retval = RET_TYPE (static_cast<octave_idx_type> (1), nc, nc); \
1687  retval.cidx (0) = 0; \
1688  for (octave_idx_type i = 0; i < nc ; i++) \
1689  { \
1690  retval.ridx (i) = 0; \
1691  retval.cidx (i+1) = i+1; \
1692  retval.data (i) = MT_RESULT; \
1693  } \
1694  } \
1695  else \
1696  retval = RET_TYPE (static_cast<octave_idx_type> (1), nc, \
1697  static_cast<octave_idx_type> (0)); \
1698  } \
1699  else if (nc == 0 && dim == 1) \
1700  { \
1701  if (MT_RESULT) \
1702  { \
1703  retval = RET_TYPE (nr, static_cast<octave_idx_type> (1), nr); \
1704  retval.cidx (0) = 0; \
1705  retval.cidx (1) = nr; \
1706  for (octave_idx_type i = 0; i < nr; i++) \
1707  { \
1708  retval.ridx (i) = i; \
1709  retval.data (i) = MT_RESULT; \
1710  } \
1711  } \
1712  else \
1713  retval = RET_TYPE (nr, static_cast<octave_idx_type> (1), \
1714  static_cast<octave_idx_type> (0)); \
1715  } \
1716  else \
1717  retval.resize (nr > 0, nc > 0); \
1718  \
1719  return retval
1720 
1721 #define SPARSE_REDUCTION_OP_ROW_EXPR(OP) \
1722  tmp[ridx (i)] OP data (i)
1723 
1724 #define SPARSE_REDUCTION_OP_COL_EXPR(OP) \
1725  tmp[j] OP data (i)
1726 
1727 #define SPARSE_REDUCTION_OP(RET_TYPE, EL_TYPE, OP, INIT_VAL, MT_RESULT) \
1728  SPARSE_BASE_REDUCTION_OP (RET_TYPE, EL_TYPE, \
1729  SPARSE_REDUCTION_OP_ROW_EXPR (OP), \
1730  SPARSE_REDUCTION_OP_COL_EXPR (OP), \
1731  INIT_VAL, MT_RESULT)
1732 
1733 // Don't break from this loop if the test succeeds because
1734 // we are looping over the rows and not the columns in the inner loop.
1735 #define SPARSE_ANY_ALL_OP_ROW_CODE(TEST_OP, TEST_TRUE_VAL) \
1736  if (data (i) TEST_OP 0.0) \
1737  tmp[ridx (i)] = TEST_TRUE_VAL;
1738 
1739 #define SPARSE_ANY_ALL_OP_COL_CODE(TEST_OP, TEST_TRUE_VAL) \
1740  if (data (i) TEST_OP 0.0) \
1741  { \
1742  tmp[j] = TEST_TRUE_VAL; \
1743  break; \
1744  }
1745 
1746 #define SPARSE_ANY_ALL_OP(DIM, INIT_VAL, MT_RESULT, TEST_OP, TEST_TRUE_VAL) \
1747  SPARSE_BASE_REDUCTION_OP (SparseBoolMatrix, char, \
1748  SPARSE_ANY_ALL_OP_ROW_CODE (TEST_OP, TEST_TRUE_VAL), \
1749  SPARSE_ANY_ALL_OP_COL_CODE (TEST_OP, TEST_TRUE_VAL), \
1750  INIT_VAL, MT_RESULT)
1751 
1752 #define SPARSE_ALL_OP(DIM) \
1753  if ((rows () == 1 && dim == -1) || dim == 1) \
1754  return transpose (). all (0). transpose (); \
1755  else \
1756  { \
1757  SPARSE_ANY_ALL_OP (DIM, (cidx (j+1) - cidx (j) < nr ? false : true), \
1758  true, ==, false); \
1759  }
1760 
1761 #define SPARSE_ANY_OP(DIM) SPARSE_ANY_ALL_OP (DIM, false, false, !=, true)
1762 
1763 #define SPARSE_SPARSE_MUL(RET_TYPE, RET_EL_TYPE, EL_TYPE) \
1764  octave_idx_type nr = m.rows (); \
1765  octave_idx_type nc = m.cols (); \
1766  \
1767  octave_idx_type a_nr = a.rows (); \
1768  octave_idx_type a_nc = a.cols (); \
1769  \
1770  if (nr == 1 && nc == 1) \
1771  { \
1772  RET_EL_TYPE s = m.elem (0,0); \
1773  octave_idx_type nz = a.nnz (); \
1774  RET_TYPE r (a_nr, a_nc, nz); \
1775  \
1776  for (octave_idx_type i = 0; i < nz; i++) \
1777  { \
1778  octave_quit (); \
1779  r.data (i) = s * a.data (i); \
1780  r.ridx (i) = a.ridx (i); \
1781  } \
1782  for (octave_idx_type i = 0; i < a_nc + 1; i++) \
1783  { \
1784  octave_quit (); \
1785  r.cidx (i) = a.cidx (i); \
1786  } \
1787  \
1788  r.maybe_compress (true); \
1789  return r; \
1790  } \
1791  else if (a_nr == 1 && a_nc == 1) \
1792  { \
1793  RET_EL_TYPE s = a.elem (0,0); \
1794  octave_idx_type nz = m.nnz (); \
1795  RET_TYPE r (nr, nc, nz); \
1796  \
1797  for (octave_idx_type i = 0; i < nz; i++) \
1798  { \
1799  octave_quit (); \
1800  r.data (i) = m.data (i) * s; \
1801  r.ridx (i) = m.ridx (i); \
1802  } \
1803  for (octave_idx_type i = 0; i < nc + 1; i++) \
1804  { \
1805  octave_quit (); \
1806  r.cidx (i) = m.cidx (i); \
1807  } \
1808  \
1809  r.maybe_compress (true); \
1810  return r; \
1811  } \
1812  else if (nc != a_nr) \
1813  octave::err_nonconformant ("operator *", nr, nc, a_nr, a_nc); \
1814  else \
1815  { \
1816  OCTAVE_LOCAL_BUFFER (octave_idx_type, w, nr); \
1817  RET_TYPE retval (nr, a_nc, static_cast<octave_idx_type> (0)); \
1818  for (octave_idx_type i = 0; i < nr; i++) \
1819  w[i] = 0; \
1820  retval.xcidx (0) = 0; \
1821  \
1822  octave_idx_type nel = 0; \
1823  \
1824  for (octave_idx_type i = 0; i < a_nc; i++) \
1825  { \
1826  for (octave_idx_type j = a.cidx (i); j < a.cidx (i+1); j++) \
1827  { \
1828  octave_idx_type col = a.ridx (j); \
1829  for (octave_idx_type k = m.cidx (col) ; k < m.cidx (col+1); k++) \
1830  { \
1831  if (w[m.ridx (k)] < i + 1) \
1832  { \
1833  w[m.ridx (k)] = i + 1; \
1834  nel++; \
1835  } \
1836  octave_quit (); \
1837  } \
1838  } \
1839  retval.xcidx (i+1) = nel; \
1840  } \
1841  \
1842  if (nel == 0) \
1843  return RET_TYPE (nr, a_nc); \
1844  else \
1845  { \
1846  for (octave_idx_type i = 0; i < nr; i++) \
1847  w[i] = 0; \
1848  \
1849  OCTAVE_LOCAL_BUFFER (RET_EL_TYPE, Xcol, nr); \
1850  \
1851  retval.change_capacity (nel); \
1852  /* The optimal break-point as estimated from simulations */ \
1853  /* Note that Mergesort is O(nz log(nz)) while searching all */ \
1854  /* values is O(nr), where nz here is nonzero per row of */ \
1855  /* length nr. The test itself was then derived from the */ \
1856  /* simulation with random square matrices and the observation */ \
1857  /* of the number of nonzero elements in the output matrix */ \
1858  /* it was found that the breakpoints were */ \
1859  /* nr: 500 1000 2000 5000 10000 */ \
1860  /* nz: 6 25 97 585 2202 */ \
1861  /* The below is a simplication of the 'polyfit'-ed parameters */ \
1862  /* to these breakpoints */ \
1863  octave_idx_type n_per_col = (a_nc > 43000 ? 43000 : \
1864  (a_nc * a_nc) / 43000); \
1865  octave_idx_type ii = 0; \
1866  octave_idx_type *ri = retval.xridx (); \
1867  octave_sort<octave_idx_type> sort; \
1868  \
1869  for (octave_idx_type i = 0; i < a_nc ; i++) \
1870  { \
1871  if (retval.xcidx (i+1) - retval.xcidx (i) > n_per_col) \
1872  { \
1873  for (octave_idx_type j = a.cidx (i); j < a.cidx (i+1); j++) \
1874  { \
1875  octave_idx_type col = a.ridx (j); \
1876  EL_TYPE tmpval = a.data (j); \
1877  for (octave_idx_type k = m.cidx (col) ; \
1878  k < m.cidx (col+1); k++) \
1879  { \
1880  octave_quit (); \
1881  octave_idx_type row = m.ridx (k); \
1882  if (w[row] < i + 1) \
1883  { \
1884  w[row] = i + 1; \
1885  Xcol[row] = tmpval * m.data (k); \
1886  } \
1887  else \
1888  Xcol[row] += tmpval * m.data (k); \
1889  } \
1890  } \
1891  for (octave_idx_type k = 0; k < nr; k++) \
1892  if (w[k] == i + 1) \
1893  { \
1894  retval.xdata (ii) = Xcol[k]; \
1895  retval.xridx (ii++) = k; \
1896  } \
1897  } \
1898  else \
1899  { \
1900  for (octave_idx_type j = a.cidx (i); j < a.cidx (i+1); j++) \
1901  { \
1902  octave_idx_type col = a.ridx (j); \
1903  EL_TYPE tmpval = a.data (j); \
1904  for (octave_idx_type k = m.cidx (col) ; \
1905  k < m.cidx (col+1); k++) \
1906  { \
1907  octave_quit (); \
1908  octave_idx_type row = m.ridx (k); \
1909  if (w[row] < i + 1) \
1910  { \
1911  w[row] = i + 1; \
1912  retval.xridx (ii++) = row; \
1913  Xcol[row] = tmpval * m.data (k); \
1914  } \
1915  else \
1916  Xcol[row] += tmpval * m.data (k); \
1917  } \
1918  } \
1919  sort.sort (ri + retval.xcidx (i), ii - retval.xcidx (i)); \
1920  for (octave_idx_type k = retval.xcidx (i); k < ii; k++) \
1921  retval.xdata (k) = Xcol[retval.xridx (k)]; \
1922  } \
1923  } \
1924  retval.maybe_compress (true); \
1925  return retval; \
1926  } \
1927  }
1928 
1929 #define SPARSE_FULL_MUL(RET_TYPE, EL_TYPE) \
1930  octave_idx_type nr = m.rows (); \
1931  octave_idx_type nc = m.cols (); \
1932  \
1933  octave_idx_type a_nr = a.rows (); \
1934  octave_idx_type a_nc = a.cols (); \
1935  \
1936  if (nr == 1 && nc == 1) \
1937  { \
1938  RET_TYPE retval = m.elem (0,0) * a; \
1939  return retval; \
1940  } \
1941  else if (nc != a_nr) \
1942  octave::err_nonconformant ("operator *", nr, nc, a_nr, a_nc); \
1943  else \
1944  { \
1945  RET_TYPE::element_type zero = RET_TYPE::element_type (); \
1946  \
1947  RET_TYPE retval (nr, a_nc, zero); \
1948  \
1949  for (octave_idx_type i = 0; i < a_nc ; i++) \
1950  { \
1951  for (octave_idx_type j = 0; j < a_nr; j++) \
1952  { \
1953  octave_quit (); \
1954  \
1955  EL_TYPE tmpval = a.elem (j,i); \
1956  for (octave_idx_type k = m.cidx (j) ; k < m.cidx (j+1); k++) \
1957  retval.elem (m.ridx (k),i) += tmpval * m.data (k); \
1958  } \
1959  } \
1960  return retval; \
1961  }
1962 
1963 #define SPARSE_FULL_TRANS_MUL(RET_TYPE, EL_TYPE, CONJ_OP) \
1964  octave_idx_type nr = m.rows (); \
1965  octave_idx_type nc = m.cols (); \
1966  \
1967  octave_idx_type a_nr = a.rows (); \
1968  octave_idx_type a_nc = a.cols (); \
1969  \
1970  if (nr == 1 && nc == 1) \
1971  { \
1972  RET_TYPE retval = CONJ_OP (m.elem (0,0)) * a; \
1973  return retval; \
1974  } \
1975  else if (nr != a_nr) \
1976  octave::err_nonconformant ("operator *", nc, nr, a_nr, a_nc); \
1977  else \
1978  { \
1979  RET_TYPE retval (nc, a_nc); \
1980  \
1981  for (octave_idx_type i = 0; i < a_nc ; i++) \
1982  { \
1983  for (octave_idx_type j = 0; j < nc; j++) \
1984  { \
1985  octave_quit (); \
1986  \
1987  EL_TYPE acc = EL_TYPE (); \
1988  for (octave_idx_type k = m.cidx (j) ; k < m.cidx (j+1); k++) \
1989  acc += a.elem (m.ridx (k),i) * CONJ_OP (m.data (k)); \
1990  retval.xelem (j,i) = acc; \
1991  } \
1992  } \
1993  return retval; \
1994  }
1995 
1996 #define FULL_SPARSE_MUL(RET_TYPE, EL_TYPE) \
1997  octave_idx_type nr = m.rows (); \
1998  octave_idx_type nc = m.cols (); \
1999  \
2000  octave_idx_type a_nr = a.rows (); \
2001  octave_idx_type a_nc = a.cols (); \
2002  \
2003  if (a_nr == 1 && a_nc == 1) \
2004  { \
2005  RET_TYPE retval = m * a.elem (0,0); \
2006  return retval; \
2007  } \
2008  else if (nc != a_nr) \
2009  octave::err_nonconformant ("operator *", nr, nc, a_nr, a_nc); \
2010  else \
2011  { \
2012  RET_TYPE::element_type zero = RET_TYPE::element_type (); \
2013  \
2014  RET_TYPE retval (nr, a_nc, zero); \
2015  \
2016  for (octave_idx_type i = 0; i < a_nc ; i++) \
2017  { \
2018  octave_quit (); \
2019  for (octave_idx_type j = a.cidx (i); j < a.cidx (i+1); j++) \
2020  { \
2021  octave_idx_type col = a.ridx (j); \
2022  EL_TYPE tmpval = a.data (j); \
2023  \
2024  for (octave_idx_type k = 0 ; k < nr; k++) \
2025  retval.xelem (k,i) += tmpval * m.elem (k,col); \
2026  } \
2027  } \
2028  return retval; \
2029  }
2030 
2031 #define FULL_SPARSE_MUL_TRANS(RET_TYPE, EL_TYPE, CONJ_OP) \
2032  octave_idx_type nr = m.rows (); \
2033  octave_idx_type nc = m.cols (); \
2034  \
2035  octave_idx_type a_nr = a.rows (); \
2036  octave_idx_type a_nc = a.cols (); \
2037  \
2038  if (a_nr == 1 && a_nc == 1) \
2039  { \
2040  RET_TYPE retval = m * CONJ_OP (a.elem (0,0)); \
2041  return retval; \
2042  } \
2043  else if (nc != a_nc) \
2044  octave::err_nonconformant ("operator *", nr, nc, a_nc, a_nr); \
2045  else \
2046  { \
2047  RET_TYPE::element_type zero = RET_TYPE::element_type (); \
2048  \
2049  RET_TYPE retval (nr, a_nr, zero); \
2050  \
2051  for (octave_idx_type i = 0; i < a_nc ; i++) \
2052  { \
2053  octave_quit (); \
2054  for (octave_idx_type j = a.cidx (i); j < a.cidx (i+1); j++) \
2055  { \
2056  octave_idx_type col = a.ridx (j); \
2057  EL_TYPE tmpval = CONJ_OP (a.data (j)); \
2058  for (octave_idx_type k = 0 ; k < nr; k++) \
2059  retval.xelem (k,col) += tmpval * m.elem (k,i); \
2060  } \
2061  } \
2062  return retval; \
2063  }
2064 
2065 #endif