1 /* ========================================================================== */
2 /* === Check/cholmod_write ================================================== */
3 /* ========================================================================== */
4
5 /* Write a matrix to a file in Matrix Market form.
6 *
7 * A can be sparse or full.
8 *
9 * If present and non-empty, A and Z must have the same dimension. Z contains
10 * the explicit zero entries in the matrix (which MATLAB drops). The entries
11 * of Z appear as explicit zeros in the output file. Z is optional. If it is
12 * an empty matrix it is ignored. Z must be sparse or empty, if present.
13 * It is ignored if A is full.
14 *
15 * filename is the name of the output file. comments is file whose
16 * contents are include after the Matrix Market header and before the first
17 * data line. Ignored if an empty string or not present.
18 *
19 * Except for the workspace used by cholmod_symmetry (ncol integers) for
20 * the sparse case, these routines use no workspace at all.
21 */
22
23 #ifndef NCHECK
24
25 #include "cholmod_config.h"
26 #include "cholmod_internal.h"
27 #include "cholmod_check.h"
28 #include "cholmod_matrixops.h"
29 #include <string.h>
30 #include <ctype.h>
31
32 #define MMLEN 1024
33 #define MAXLINE MMLEN+6
34
35 /* ========================================================================== */
36 /* === include_comments ===================================================== */
37 /* ========================================================================== */
38
39 /* Read in the comments file, if it exists, and copy it to the Matrix Market
40 * file. A "%" is prepended to each line. Returns TRUE if successful, FALSE
41 * otherwise.
42 */
43
include_comments(FILE * f,const char * comments)44 static int include_comments (FILE *f, const char *comments)
45 {
46 FILE *cf = NULL ;
47 char buffer [MAXLINE] ;
48 int ok = TRUE ;
49 if (comments != NULL && comments [0] != '\0')
50 {
51 cf = fopen (comments, "r") ;
52 if (cf == NULL)
53 {
54 return (FALSE) ;
55 }
56 while (ok && fgets (buffer, MAXLINE, cf) != NULL)
57 {
58 /* ensure the line is not too long */
59 buffer [MMLEN-1] = '\0' ;
60 buffer [MMLEN-2] = '\n' ;
61 ok = ok && (fprintf (f, "%%%s", buffer) > 0) ;
62 }
63 fclose (cf) ;
64 }
65 return (ok) ;
66 }
67
68
69 /* ========================================================================== */
70 /* === get_value ============================================================ */
71 /* ========================================================================== */
72
73 /* Get the pth value in the matrix. */
74
get_value(double * Ax,double * Az,Int p,Int xtype,double * x,double * z)75 static void get_value
76 (
77 double *Ax, /* real values, or real/imag. for CHOLMOD_COMPLEX type */
78 double *Az, /* imaginary values for CHOLMOD_ZOMPLEX type */
79 Int p, /* get the pth entry */
80 Int xtype, /* A->xtype: pattern, real, complex, or zomplex */
81 double *x, /* the real part */
82 double *z /* the imaginary part */
83 )
84 {
85 switch (xtype)
86 {
87 case CHOLMOD_PATTERN:
88 *x = 1 ;
89 *z = 0 ;
90 break ;
91
92 case CHOLMOD_REAL:
93 *x = Ax [p] ;
94 *z = 0 ;
95 break ;
96
97 case CHOLMOD_COMPLEX:
98 *x = Ax [2*p] ;
99 *z = Ax [2*p+1] ;
100 break ;
101
102 case CHOLMOD_ZOMPLEX:
103 *x = Ax [p] ;
104 *z = Az [p] ;
105 break ;
106 }
107 }
108
109
110 /* ========================================================================== */
111 /* === print_value ========================================================== */
112 /* ========================================================================== */
113
114 /* Print a numeric value to the file, using the shortest format that ensures
115 * the value is written precisely. Returns TRUE if successful, FALSE otherwise.
116 */
117
print_value(FILE * f,double x,Int is_integer)118 static int print_value
119 (
120 FILE *f, /* file to print to */
121 double x, /* value to print */
122 Int is_integer /* TRUE if printing as an integer */
123 )
124 {
125 double y ;
126 char s [MAXLINE], *p ;
127 Int i, dest = 0, src = 0 ;
128 int width, ok ;
129
130 if (is_integer)
131 {
132 i = (Int) x ;
133 ok = (fprintf (f, ID, i) > 0) ;
134 return (ok) ;
135 }
136
137 /* ---------------------------------------------------------------------- */
138 /* handle Inf and NaN */
139 /* ---------------------------------------------------------------------- */
140
141 /* change -inf to -HUGE_DOUBLE, and change +inf and nan to +HUGE_DOUBLE */
142 if (CHOLMOD_IS_NAN (x) || x >= HUGE_DOUBLE)
143 {
144 x = HUGE_DOUBLE ;
145 }
146 else if (x <= -HUGE_DOUBLE)
147 {
148 x = -HUGE_DOUBLE ;
149 }
150
151 /* ---------------------------------------------------------------------- */
152 /* find the smallest acceptable precision */
153 /* ---------------------------------------------------------------------- */
154
155 for (width = 6 ; width < 20 ; width++)
156 {
157 sprintf (s, "%.*g", width, x) ;
158 sscanf (s, "%lg", &y) ;
159 if (x == y) break ;
160 }
161
162 /* ---------------------------------------------------------------------- */
163 /* shorten the string */
164 /* ---------------------------------------------------------------------- */
165
166 /* change "e+0" to "e", change "e+" to "e", and change "e-0" to "e-" */
167 for (i = 0 ; i < MAXLINE && s [i] != '\0' ; i++)
168 {
169 if (s [i] == 'e')
170 {
171 if (s [i+1] == '+')
172 {
173 dest = i+1 ;
174 if (s [i+2] == '0')
175 {
176 /* delete characters s[i+1] and s[i+2] */
177 src = i+3 ;
178 }
179 else
180 {
181 /* delete characters s[i+1] */
182 src = i+2 ;
183 }
184 }
185 else if (s [i+1] == '-')
186 {
187 dest = i+2 ;
188 if (s [i+2] == '0')
189 {
190 /* delete character s[i+2] */
191 src = i+3 ;
192 }
193 else
194 {
195 /* no change */
196 break ;
197 }
198 }
199 while (s [src] != '\0')
200 {
201 s [dest++] = s [src++] ;
202 }
203 s [dest] = '\0' ;
204 break ;
205 }
206 }
207
208 /* delete the leading "0" if present and not necessary */
209 p = s ;
210 s [MAXLINE-1] = '\0' ;
211 i = strlen (s) ;
212 if (i > 2 && s [0] == '0' && s [1] == '.')
213 {
214 /* change "0.x" to ".x" */
215 p = s + 1 ;
216 }
217 else if (i > 3 && s [0] == '-' && s [1] == '0' && s [2] == '.')
218 {
219 /* change "-0.x" to "-.x" */
220 s [1] = '-' ;
221 p = s + 1 ;
222 }
223
224 #if 0
225 /* double-check */
226 i = sscanf (p, "%lg", &z) ;
227 if (i != 1 || y != z)
228 {
229 /* oops! something went wrong in the "e+0" edit, above. */
230 /* this "cannot" happen */
231 sprintf (s, "%.*g", width, x) ;
232 p = s ;
233 }
234 #endif
235
236 /* ---------------------------------------------------------------------- */
237 /* print the value to the file */
238 /* ---------------------------------------------------------------------- */
239
240 ok = (fprintf (f, "%s", p) > 0) ;
241 return (ok) ;
242 }
243
244
245 /* ========================================================================== */
246 /* === print_triplet ======================================================== */
247 /* ========================================================================== */
248
249 /* Print a triplet, converting it to one-based. Returns TRUE if successful,
250 * FALSE otherwise.
251 */
252
print_triplet(FILE * f,Int is_binary,Int is_complex,Int is_integer,Int i,Int j,double x,double z)253 static int print_triplet
254 (
255 FILE *f, /* file to print to */
256 Int is_binary, /* TRUE if file is "pattern" */
257 Int is_complex, /* TRUE if file is "complex" */
258 Int is_integer, /* TRUE if file is "integer" */
259 Int i, /* row index (zero-based) */
260 Int j, /* column index (zero-based) */
261 double x, /* real part */
262 double z /* imaginary part */
263 )
264 {
265 int ok ;
266 ok = (fprintf (f, ID " " ID, 1+i, 1+j) > 0) ;
267 if (!is_binary)
268 {
269 fprintf (f, " ") ;
270 ok = ok && print_value (f, x, is_integer) ;
271 if (is_complex)
272 {
273 fprintf (f, " ") ;
274 ok = ok && print_value (f, z, is_integer) ;
275 }
276 }
277 ok = ok && (fprintf (f, "\n") > 0) ;
278 return (ok) ;
279 }
280
281
282 /* ========================================================================== */
283 /* === ntriplets ============================================================ */
284 /* ========================================================================== */
285
286 /* Compute the number of triplets that will be printed to the file
287 * from the matrix A. */
288
ntriplets(cholmod_sparse * A,Int is_sym)289 static Int ntriplets
290 (
291 cholmod_sparse *A, /* matrix that will be printed */
292 Int is_sym /* TRUE if the file is symmetric (lower part only)*/
293 )
294 {
295 Int *Ap, *Ai, *Anz, packed, i, j, p, pend, ncol, stype, nz = 0 ;
296 if (A == NULL)
297 {
298 /* the Z matrix is NULL */
299 return (0) ;
300 }
301 stype = A->stype ;
302 Ap = A->p ;
303 Ai = A->i ;
304 Anz = A->nz ;
305 packed = A->packed ;
306 ncol = A->ncol ;
307 for (j = 0 ; j < ncol ; j++)
308 {
309 p = Ap [j] ;
310 pend = (packed) ? Ap [j+1] : p + Anz [j] ;
311 for ( ; p < pend ; p++)
312 {
313 i = Ai [p] ;
314 if ((stype < 0 && i >= j) || (stype == 0 && (i >= j || !is_sym)))
315 {
316 /* CHOLMOD matrix is symmetric-lower (and so is the file);
317 * or CHOLMOD matrix is unsymmetric and either A(i,j) is in
318 * the lower part or the file is unsymmetric. */
319 nz++ ;
320 }
321 else if (stype > 0 && i <= j)
322 {
323 /* CHOLMOD matrix is symmetric-upper, but the file is
324 * symmetric-lower. Need to transpose the entry. */
325 nz++ ;
326 }
327 }
328 }
329 return (nz) ;
330 }
331
332
333 /* ========================================================================== */
334 /* === cholmod_write_sparse ================================================= */
335 /* ========================================================================== */
336
337 /* Write a sparse matrix to a file in Matrix Market format. Optionally include
338 * comments, and print explicit zero entries given by the pattern of the Z
339 * matrix. If not NULL, the Z matrix must have the same dimensions and stype
340 * as A.
341 *
342 * Returns the symmetry in which the matrix was printed (1 to 7, see the
343 * CHOLMOD_MM_* codes in CHOLMOD/Include/cholmod_core.h), or -1 on failure.
344 *
345 * If A and Z are sorted on input, and either unsymmetric (stype = 0) or
346 * symmetric-lower (stype < 0), and if A and Z do not overlap, then the triplets
347 * are sorted, first by column and then by row index within each column, with
348 * no duplicate entries. If all the above holds except stype > 0, then the
349 * triplets are sorted by row first and then column.
350 */
351
CHOLMOD(write_sparse)352 int CHOLMOD(write_sparse)
353 (
354 /* ---- input ---- */
355 FILE *f, /* file to write to, must already be open */
356 cholmod_sparse *A, /* matrix to print */
357 cholmod_sparse *Z, /* optional matrix with pattern of explicit zeros */
358 const char *comments, /* optional filename of comments to include */
359 /* --------------- */
360 cholmod_common *Common
361 )
362 {
363 double x = 0, z = 0 ;
364 double *Ax, *Az ;
365 Int *Ap, *Ai, *Anz, *Zp, *Zi, *Znz ;
366 Int nrow, ncol, is_complex, symmetry, i, j, q, iz, p, nz, is_binary, stype,
367 is_integer, asym, is_sym, xtype, apacked, zpacked, pend, qend, zsym ;
368 int ok ;
369
370 /* ---------------------------------------------------------------------- */
371 /* check inputs */
372 /* ---------------------------------------------------------------------- */
373
374 RETURN_IF_NULL_COMMON (EMPTY) ;
375 RETURN_IF_NULL (f, EMPTY) ;
376 RETURN_IF_NULL (A, EMPTY) ;
377 RETURN_IF_XTYPE_INVALID (A, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, EMPTY) ;
378 if (Z != NULL && (Z->nrow == 0 || Z->ncol == 0))
379 {
380 /* Z is non-NULL but empty, so treat it as a NULL matrix */
381 Z = NULL ;
382 }
383 if (Z != NULL)
384 {
385 RETURN_IF_XTYPE_INVALID (Z, CHOLMOD_PATTERN, CHOLMOD_ZOMPLEX, EMPTY) ;
386 if (Z->nrow != A->nrow || Z->ncol != A->ncol || Z->stype != A->stype)
387 {
388 ERROR (CHOLMOD_INVALID, "dimension or type of A and Z mismatch") ;
389 return (EMPTY) ;
390 }
391 }
392 Common->status = CHOLMOD_OK ;
393
394 /* ---------------------------------------------------------------------- */
395 /* get the A matrix */
396 /* ---------------------------------------------------------------------- */
397
398 Ap = A->p ;
399 Ai = A->i ;
400 Ax = A->x ;
401 Az = A->z ;
402 Anz = A->nz ;
403 nrow = A->nrow ;
404 ncol = A->ncol ;
405 xtype = A->xtype ;
406 apacked = A->packed ;
407
408 if (xtype == CHOLMOD_PATTERN)
409 {
410 /* a CHOLMOD pattern matrix is printed as "pattern" in the file */
411 is_binary = TRUE ;
412 is_integer = FALSE ;
413 is_complex = FALSE ;
414 }
415 else if (xtype == CHOLMOD_REAL)
416 {
417 /* determine if a real matrix is in fact binary or integer */
418 is_binary = TRUE ;
419 is_integer = TRUE ;
420 is_complex = FALSE ;
421 for (j = 0 ; (is_binary || is_integer) && j < ncol ; j++)
422 {
423 p = Ap [j] ;
424 pend = (apacked) ? Ap [j+1] : p + Anz [j] ;
425 for ( ; (is_binary || is_integer) && p < pend ; p++)
426 {
427 x = Ax [p] ;
428 if (x != 1)
429 {
430 is_binary = FALSE ;
431 }
432 /* convert to Int and then back to double */
433 i = (Int) x ;
434 z = (double) i ;
435 if (z != x)
436 {
437 is_integer = FALSE ;
438 }
439 }
440 }
441 }
442 else
443 {
444 /* a CHOLMOD complex matrix is printed as "complex" in the file */
445 is_binary = FALSE ;
446 is_integer = FALSE ;
447 is_complex = TRUE ;
448 }
449
450 /* ---------------------------------------------------------------------- */
451 /* get the Z matrix (only consider the pattern) */
452 /* ---------------------------------------------------------------------- */
453
454 Zp = NULL ;
455 Zi = NULL ;
456 Znz = NULL ;
457 zpacked = TRUE ;
458 if (Z != NULL)
459 {
460 Zp = Z->p ;
461 Zi = Z->i ;
462 Znz = Z->nz ;
463 zpacked = Z->packed ;
464 }
465
466 /* ---------------------------------------------------------------------- */
467 /* determine the symmetry of A and Z */
468 /* ---------------------------------------------------------------------- */
469
470 stype = A->stype ;
471 if (A->nrow != A->ncol)
472 {
473 asym = CHOLMOD_MM_RECTANGULAR ;
474 }
475 else if (stype != 0)
476 {
477 /* CHOLMOD's A and Z matrices have a symmetric (and matching) stype.
478 * Note that the diagonal is not checked. */
479 asym = is_complex ? CHOLMOD_MM_HERMITIAN : CHOLMOD_MM_SYMMETRIC ;
480 }
481 else if (!A->sorted)
482 {
483 /* A is in unsymmetric storage, but unsorted */
484 asym = CHOLMOD_MM_UNSYMMETRIC ;
485 }
486 else
487 {
488 /* CHOLMOD's stype is zero (stored in unsymmetric form) */
489 asym = EMPTY ;
490 zsym = EMPTY ;
491
492 #ifndef NMATRIXOPS
493 /* determine if the matrices are in fact symmetric or Hermitian */
494 asym = CHOLMOD(symmetry) (A, 1, NULL, NULL, NULL, NULL, Common) ;
495 zsym = (Z == NULL) ? 999 :
496 CHOLMOD(symmetry) (Z, 1, NULL, NULL, NULL, NULL, Common) ;
497 #endif
498
499 if (asym == EMPTY || zsym <= CHOLMOD_MM_UNSYMMETRIC)
500 {
501 /* not computed, out of memory, or Z is unsymmetric */
502 asym = CHOLMOD_MM_UNSYMMETRIC ;
503 }
504 }
505
506 /* ---------------------------------------------------------------------- */
507 /* write the Matrix Market header */
508 /* ---------------------------------------------------------------------- */
509
510 ok = fprintf (f, "%%%%MatrixMarket matrix coordinate") > 0 ;
511
512 if (is_complex)
513 {
514 ok = ok && (fprintf (f, " complex") > 0) ;
515 }
516 else if (is_binary)
517 {
518 ok = ok && (fprintf (f, " pattern") > 0) ;
519 }
520 else if (is_integer)
521 {
522 ok = ok && (fprintf (f, " integer") > 0) ;
523 }
524 else
525 {
526 ok = ok && (fprintf (f, " real") > 0) ;
527 }
528
529 is_sym = FALSE ;
530
531 switch (asym)
532 {
533 case CHOLMOD_MM_RECTANGULAR:
534 case CHOLMOD_MM_UNSYMMETRIC:
535 /* A is rectangular or unsymmetric */
536 ok = ok && (fprintf (f, " general\n") > 0) ;
537 is_sym = FALSE ;
538 symmetry = CHOLMOD_MM_UNSYMMETRIC ;
539 break ;
540
541 case CHOLMOD_MM_SYMMETRIC:
542 case CHOLMOD_MM_SYMMETRIC_POSDIAG:
543 /* A is symmetric */
544 ok = ok && (fprintf (f, " symmetric\n") > 0) ;
545 is_sym = TRUE ;
546 symmetry = CHOLMOD_MM_SYMMETRIC ;
547 break ;
548
549 case CHOLMOD_MM_HERMITIAN:
550 case CHOLMOD_MM_HERMITIAN_POSDIAG:
551 /* A is Hermitian */
552 ok = ok && (fprintf (f, " Hermitian\n") > 0) ;
553 is_sym = TRUE ;
554 symmetry = CHOLMOD_MM_HERMITIAN ;
555 break ;
556
557 case CHOLMOD_MM_SKEW_SYMMETRIC:
558 /* A is skew symmetric */
559 ok = ok && (fprintf (f, " skew-symmetric\n") > 0) ;
560 is_sym = TRUE ;
561 symmetry = CHOLMOD_MM_SKEW_SYMMETRIC ;
562 break ;
563 }
564
565 /* ---------------------------------------------------------------------- */
566 /* include the comments if present */
567 /* ---------------------------------------------------------------------- */
568
569 ok = ok && include_comments (f, comments) ;
570
571 /* ---------------------------------------------------------------------- */
572 /* write a sparse matrix (A and Z) */
573 /* ---------------------------------------------------------------------- */
574
575 nz = ntriplets (A, is_sym) + ntriplets (Z, is_sym) ;
576
577 /* write the first data line, with nrow, ncol, and # of triplets */
578 ok = ok && (fprintf (f, ID " " ID " " ID "\n", nrow, ncol, nz) > 0) ;
579
580 for (j = 0 ; ok && j < ncol ; j++)
581 {
582 /* merge column of A and Z */
583 p = Ap [j] ;
584 pend = (apacked) ? Ap [j+1] : p + Anz [j] ;
585 q = (Z == NULL) ? 0 : Zp [j] ;
586 qend = (Z == NULL) ? 0 : ((zpacked) ? Zp [j+1] : q + Znz [j]) ;
587 while (ok)
588 {
589 /* get the next row index from A and Z */
590 i = (p < pend) ? Ai [p] : (nrow+1) ;
591 iz = (q < qend) ? Zi [q] : (nrow+2) ;
592 if (i <= iz)
593 {
594 /* get A(i,j), or quit if both A and Z are exhausted */
595 if (i == nrow+1) break ;
596 get_value (Ax, Az, p, xtype, &x, &z) ;
597 p++ ;
598 }
599 else
600 {
601 /* get Z(i,j) */
602 i = iz ;
603 x = 0 ;
604 z = 0 ;
605 q++ ;
606 }
607 if ((stype < 0 && i >= j) || (stype == 0 && (i >= j || !is_sym)))
608 {
609 /* CHOLMOD matrix is symmetric-lower (and so is the file);
610 * or CHOLMOD matrix is unsymmetric and either A(i,j) is in
611 * the lower part or the file is unsymmetric. */
612 ok = ok && print_triplet (f, is_binary, is_complex, is_integer,
613 i,j, x,z) ;
614 }
615 else if (stype > 0 && i <= j)
616 {
617 /* CHOLMOD matrix is symmetric-upper, but the file is
618 * symmetric-lower. Need to transpose the entry. If the
619 * matrix is real, the complex part is ignored. If the matrix
620 * is complex, it Hermitian.
621 */
622 ASSERT (IMPLIES (is_complex, asym == CHOLMOD_MM_HERMITIAN)) ;
623 if (z != 0)
624 {
625 z = -z ;
626 }
627 ok = ok && print_triplet (f, is_binary, is_complex, is_integer,
628 j,i, x,z) ;
629 }
630 }
631 }
632
633 if (!ok)
634 {
635 ERROR (CHOLMOD_INVALID, "error reading/writing file") ;
636 return (EMPTY) ;
637 }
638
639 return (asym) ;
640 }
641
642
643 /* ========================================================================== */
644 /* === cholmod_write_dense ================================================== */
645 /* ========================================================================== */
646
647 /* Write a dense matrix to a file in Matrix Market format. Optionally include
648 * comments. Returns > 0 if successful, -1 otherwise (1 if rectangular, 2 if
649 * square). Future versions may return 1 to 7 on success (a CHOLMOD_MM_* code,
650 * just as cholmod_write_sparse does).
651 *
652 * A dense matrix is written in "general" format; symmetric formats in the
653 * Matrix Market standard are not exploited.
654 */
655
CHOLMOD(write_dense)656 int CHOLMOD(write_dense)
657 (
658 /* ---- input ---- */
659 FILE *f, /* file to write to, must already be open */
660 cholmod_dense *X, /* matrix to print */
661 const char *comments, /* optional filename of comments to include */
662 /* --------------- */
663 cholmod_common *Common
664 )
665 {
666 double x = 0, z = 0 ;
667 double *Xx, *Xz ;
668 Int nrow, ncol, is_complex, i, j, xtype, p ;
669 int ok ;
670
671 /* ---------------------------------------------------------------------- */
672 /* check inputs */
673 /* ---------------------------------------------------------------------- */
674
675 RETURN_IF_NULL_COMMON (EMPTY) ;
676 RETURN_IF_NULL (f, EMPTY) ;
677 RETURN_IF_NULL (X, EMPTY) ;
678 RETURN_IF_XTYPE_INVALID (X, CHOLMOD_REAL, CHOLMOD_ZOMPLEX, EMPTY) ;
679 Common->status = CHOLMOD_OK ;
680
681 /* ---------------------------------------------------------------------- */
682 /* get the X matrix */
683 /* ---------------------------------------------------------------------- */
684
685 Xx = X->x ;
686 Xz = X->z ;
687 nrow = X->nrow ;
688 ncol = X->ncol ;
689 xtype = X->xtype ;
690 is_complex = (xtype == CHOLMOD_COMPLEX) || (xtype == CHOLMOD_ZOMPLEX) ;
691
692 /* ---------------------------------------------------------------------- */
693 /* write the Matrix Market header */
694 /* ---------------------------------------------------------------------- */
695
696 ok = (fprintf (f, "%%%%MatrixMarket matrix array") > 0) ;
697 if (is_complex)
698 {
699 ok = ok && (fprintf (f, " complex general\n") > 0) ;
700 }
701 else
702 {
703 ok = ok && (fprintf (f, " real general\n") > 0) ;
704 }
705
706 /* ---------------------------------------------------------------------- */
707 /* include the comments if present */
708 /* ---------------------------------------------------------------------- */
709
710 ok = ok && include_comments (f, comments) ;
711
712 /* ---------------------------------------------------------------------- */
713 /* write a dense matrix */
714 /* ---------------------------------------------------------------------- */
715
716 /* write the first data line, with nrow and ncol */
717 ok = ok && (fprintf (f, ID " " ID "\n", nrow, ncol) > 0) ;
718
719 Xx = X->x ;
720 Xz = X->z ;
721 for (j = 0 ; ok && j < ncol ; j++)
722 {
723 for (i = 0 ; ok && i < nrow ; i++)
724 {
725 p = i + j*nrow ;
726 get_value (Xx, Xz, p, xtype, &x, &z) ;
727 ok = ok && print_value (f, x, FALSE) ;
728 if (is_complex)
729 {
730 ok = ok && (fprintf (f, " ") > 0) ;
731 ok = ok && print_value (f, z, FALSE) ;
732 }
733 ok = ok && (fprintf (f, "\n") > 0) ;
734 }
735 }
736
737 if (!ok)
738 {
739 ERROR (CHOLMOD_INVALID, "error reading/writing file") ;
740 return (EMPTY) ;
741 }
742
743 return ((nrow == ncol) ? CHOLMOD_MM_UNSYMMETRIC : CHOLMOD_MM_RECTANGULAR) ;
744 }
745 #endif
746