1 //------------------------------------------------------------------------------
2 // GB_matvec_check: print a GraphBLAS matrix and check if it is valid
3 //------------------------------------------------------------------------------
4 
5 // SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
6 // SPDX-License-Identifier: Apache-2.0
7 
8 //------------------------------------------------------------------------------
9 
10 // for code development only:
11 // #define GB_DEVELOPER 1
12 
13 #include "GB_Pending.h"
14 #include "GB.h"
15 
16 GB_PUBLIC   // accessed by the MATLAB tests in GraphBLAS/Test only
GB_matvec_check(const GrB_Matrix A,const char * name,int pr,FILE * f,const char * kind)17 GrB_Info GB_matvec_check    // check a GraphBLAS matrix or vector
18 (
19     const GrB_Matrix A,     // GraphBLAS matrix to print and check
20     const char *name,       // name of the matrix, optional
21     int pr,                 // print level; if negative, ignore nzombie
22                             // conditions and use GB_FLIP(pr) for diagnostics
23     FILE *f,                // file for output
24     const char *kind        // "matrix" or "vector"
25 )
26 {
27 
28     //--------------------------------------------------------------------------
29     // decide what to print
30     //--------------------------------------------------------------------------
31 
32     bool is_hyper = GB_IS_HYPERSPARSE (A) ;
33     bool is_full = GB_IS_FULL (A) ;
34     bool is_bitmap = GB_IS_BITMAP (A) ;
35     bool is_sparse = GB_IS_SPARSE (A) ;
36 
37     bool ignore_zombies = false ;
38     if (pr < 0)
39     {
40         pr = GB_FLIP (pr) ;
41         ignore_zombies = true ;
42     }
43     pr = GB_IMIN (pr, GxB_COMPLETE_VERBOSE) ;
44     bool phantom = (is_full && A->x == NULL) ;
45     if (phantom)
46     {
47         // convert GxB_COMPLETE* to GxB_SHORT*
48         if (pr == GxB_COMPLETE_VERBOSE) pr = GxB_SHORT_VERBOSE ;
49         if (pr == GxB_COMPLETE        ) pr = GxB_SHORT ;
50     }
51     bool pr_silent   = (pr == GxB_SILENT) ;
52     bool pr_complete = (pr == GxB_COMPLETE || pr == GxB_COMPLETE_VERBOSE) ;
53     bool pr_short    = (pr == GxB_SHORT    || pr == GxB_SHORT_VERBOSE   ) ;
54     bool one_based = GB_Global_print_one_based_get ( ) ;
55     int64_t offset = (one_based) ? 1 : 0 ;
56     #if GB_DEVELOPER
57     int pr_type = pr ;
58     #else
59     int pr_type = 0 ;
60     #endif
61 
62     GBPR0 ("\n  " GBd "x" GBd " GraphBLAS %s %s",
63         (A != NULL) ? GB_NROWS (A) : 0,
64         (A != NULL) ? GB_NCOLS (A) : 0,
65         (A != NULL && A->type != NULL && A->type->name != NULL) ?
66          A->type->name : "", kind) ;
67 
68     //--------------------------------------------------------------------------
69     // check if null, freed, or uninitialized
70     //--------------------------------------------------------------------------
71 
72     if (A == NULL)
73     {
74         GBPR0 (" NULL\n") ;
75         return (GrB_NULL_POINTER) ;
76     }
77 
78     GB_CHECK_MAGIC (A, kind) ;
79 
80     //--------------------------------------------------------------------------
81     // print the header
82     //--------------------------------------------------------------------------
83 
84     if (is_full)
85     {
86         // A->p, A->h, A->i, and A->b all null
87         GBPR0 (", full") ;
88     }
89     else if (is_bitmap)
90     {
91         // A->b not null
92         GBPR0 (", bitmap") ;
93     }
94     else if (is_sparse)
95     {
96         // A->h null, A->p not null
97         GBPR0 (", sparse") ;
98     }
99     else if (is_hyper)
100     {
101         // A->h not null
102         GBPR0 (", hypersparse") ;
103     }
104     else
105     {
106         // A is not hyper, sparse, bitmap, or full
107         GBPR0 (" invalid structure\n") ;
108         return (GrB_INVALID_OBJECT) ;
109     }
110     if (A->jumbled)
111     {
112         GBPR0 (" (jumbled)") ;
113     }
114     GBPR0 (" %s\n", A->is_csc ? "by col" : "by row") ;
115 
116     #if GB_DEVELOPER
117     GBPR0 ("  max # entries: " GBd "\n", A->nzmax) ;
118     GBPR0 ("  vlen: " GBd , A->vlen) ;
119     if (A->nvec_nonempty != -1)
120     {
121         GBPR0 (" nvec_nonempty: " GBd , A->nvec_nonempty) ;
122     }
123     GBPR0 (" nvec: " GBd " plen: " GBd  " vdim: " GBd "\n  hyper_switch %g "
124         "bitmap_switch %g\n",
125         A->nvec, A->plen, A->vdim, A->hyper_switch, A->bitmap_switch) ;
126     #endif
127 
128     switch (A->sparsity)
129     {
130 
131         // 1
132         case GxB_HYPERSPARSE :
133             GBPR0 ("  sparsity control: hypersparse only\n") ;
134             break ;
135 
136         // 2
137         case GxB_SPARSE :
138             GBPR0 ("  sparsity control: sparse only\n") ;
139             break ;
140 
141         // 3
142         case GxB_HYPERSPARSE + GxB_SPARSE :
143             GBPR0 ("  sparsity control: sparse/hypersparse\n") ;
144             break ;
145 
146         // 4
147         case GxB_BITMAP :
148             GBPR0 ("  sparsity control: bitmap only\n") ;
149             break ;
150 
151         // 5
152         case GxB_HYPERSPARSE + GxB_BITMAP :
153             GBPR0 ("  sparsity control: hypersparse/bitmap\n") ;
154             break ;
155 
156         // 6
157         case GxB_SPARSE + GxB_BITMAP :
158             GBPR0 ("  sparsity control: sparse/bitmap\n") ;
159             break ;
160 
161         // 7
162         case GxB_HYPERSPARSE + GxB_SPARSE + GxB_BITMAP :
163             GBPR0 ("  sparsity control: hypersparse/sparse/bitmap\n") ;
164             break ;
165 
166         // 8
167         case GxB_FULL :
168             GBPR0 ("  sparsity control: full\n") ;
169             break ;
170 
171         // 9
172         case GxB_HYPERSPARSE + GxB_FULL :
173             GBPR0 ("  sparsity control: hypersparse/full\n") ;
174             break ;
175 
176         // 10
177         case GxB_SPARSE + GxB_FULL :
178             GBPR0 ("  sparsity control: sparse/full\n") ;
179             break ;
180 
181         // 11
182         case GxB_HYPERSPARSE + GxB_SPARSE + GxB_FULL :
183             GBPR0 ("  sparsity control: hypersparse/sparse/full\n") ;
184             break ;
185 
186         // 12
187         case GxB_FULL + GxB_BITMAP :
188             GBPR0 ("  sparsity control: bitmap/full\n") ;
189             break ;
190 
191         // 13
192         case GxB_HYPERSPARSE + GxB_BITMAP + GxB_FULL :
193             GBPR0 ("  sparsity control: hypersparse/bitmap/full\n") ;
194             break ;
195 
196         // 14
197         case GxB_SPARSE + GxB_BITMAP + GxB_FULL :
198             GBPR0 ("  sparsity control: sparse/bitmap/full\n") ;
199             break ;
200 
201         // 15
202         case GxB_HYPERSPARSE + GxB_SPARSE + GxB_BITMAP + GxB_FULL :
203             #if GB_DEVELOPER
204             GBPR0 ("  sparsity control: hyper/sparse/bitmap/full\n") ;
205             #endif
206             break ;
207 
208         default :
209             // invalid sparsity control
210             GBPR0 ("  sparsity control: invalid\n") ;
211             return (GrB_INVALID_OBJECT) ;
212             break ;
213     }
214 
215     //--------------------------------------------------------------------------
216     // check the dimensions
217     //--------------------------------------------------------------------------
218 
219     if (A->vlen < 0 || A->vlen > GxB_INDEX_MAX ||
220         A->vdim < 0 || A->vdim > GxB_INDEX_MAX ||
221         A->nzmax < 0 || A->nzmax > GxB_INDEX_MAX)
222     {
223         GBPR0 ("  invalid %s dimensions\n", kind) ;
224         return (GrB_INVALID_OBJECT) ;
225     }
226 
227     //--------------------------------------------------------------------------
228     // check vector structure
229     //--------------------------------------------------------------------------
230 
231     if (is_full)
232     {
233         // A is full
234         if (! (A->nvec == A->vdim && A->plen == -1))
235         {
236             GBPR0 ("  invalid full %s structure\n", kind) ;
237             return (GrB_INVALID_OBJECT) ;
238         }
239     }
240     else if (is_bitmap)
241     {
242         // A is bitmap
243         if (! (A->nvec == A->vdim && A->plen == -1 &&
244                A->h == NULL && A->p == NULL && A->i == NULL))
245         {
246             GBPR0 ("  invalid bitmap %s structure\n", kind) ;
247             return (GrB_INVALID_OBJECT) ;
248         }
249     }
250     else if (is_sparse)
251     {
252         // A is sparse
253         if (! (A->nvec == A->plen && A->plen == A->vdim))
254         {
255             GBPR0 ("  invalid sparse %s structure\n", kind) ;
256             return (GrB_INVALID_OBJECT) ;
257         }
258     }
259     else
260     {
261         // A is hypersparse
262         if (! (A->nvec >= 0 && A->nvec <= A->plen && A->plen <= A->vdim))
263         {
264             GBPR0 ("  invalid hypersparse %s structure\n", kind) ;
265             return (GrB_INVALID_OBJECT) ;
266         }
267     }
268 
269     //--------------------------------------------------------------------------
270     // count the allocated blocks
271     //--------------------------------------------------------------------------
272 
273     GB_Pending Pending = A->Pending ;
274 
275     // a matrix contains 0 to 10 dynamically malloc'd blocks
276     int64_t nallocs = 0 ;
277     bool ok = true ;
278 
279     if (!A->static_header)
280     {
281         nallocs++ ;
282         #ifdef GB_DEBUG
283         ok = ok && (A->header_size == GB_Global_memtable_size (A)) ;
284         #endif
285     }
286 
287     if (A->p != NULL && !A->p_shallow)
288     {
289         nallocs++ ;
290         #ifdef GB_DEBUG
291         ok = ok && (A->p_size == GB_Global_memtable_size (A->p)) ;
292         #endif
293     }
294 
295     if (A->h != NULL && !A->h_shallow)
296     {
297         nallocs++ ;
298         #ifdef GB_DEBUG
299         ok = ok && (A->h_size == GB_Global_memtable_size (A->h)) ;
300         #endif
301     }
302 
303     if (A->b != NULL && !A->b_shallow)
304     {
305         nallocs++ ;
306         #ifdef GB_DEBUG
307         ok = ok && (A->b_size == GB_Global_memtable_size (A->b)) ;
308         #endif
309     }
310 
311     if (A->i != NULL && !A->i_shallow)
312     {
313         nallocs++ ;
314         #ifdef GB_DEBUG
315         ok = ok && (A->i_size == GB_Global_memtable_size (A->i)) ;
316         #endif
317     }
318 
319     if (A->x != NULL && !A->x_shallow)
320     {
321         nallocs++ ;
322         #ifdef GB_DEBUG
323         ok = ok && (A->x_size == GB_Global_memtable_size (A->x)) ;
324         #endif
325     }
326 
327     if (Pending != NULL)
328     {
329         nallocs++ ;
330         #ifdef GB_DEBUG
331         ok = ok && (Pending->header_size == GB_Global_memtable_size (Pending)) ;
332         #endif
333     }
334 
335     if (Pending != NULL && Pending->i != NULL)
336     {
337         nallocs++ ;
338         #ifdef GB_DEBUG
339         ok = ok && (Pending->i_size == GB_Global_memtable_size (Pending->i)) ;
340         #endif
341     }
342 
343     if (Pending != NULL && Pending->j != NULL)
344     {
345         nallocs++ ;
346         #ifdef GB_DEBUG
347         ok = ok && (Pending->j_size == GB_Global_memtable_size (Pending->j)) ;
348         #endif
349     }
350 
351     if (Pending != NULL && Pending->x != NULL)
352     {
353         nallocs++ ;
354         #ifdef GB_DEBUG
355         ok = ok && (Pending->x_size == GB_Global_memtable_size (Pending->x)) ;
356         #endif
357     }
358 
359     #if GB_DEVELOPER
360     if (pr_short || pr_complete)
361     {
362         if (A->static_header)
363         {
364             GBPR ("  static header,") ;
365         }
366         else
367         {
368             GBPR ("  header %p", A) ;
369         }
370         GBPR (" number of memory blocks: " GBd "\n", nallocs) ;
371     }
372     #endif
373 
374     if (!ok)
375     {
376         GBPR0 ("  internal memory error\n") ;
377         return (GrB_INVALID_OBJECT) ;
378     }
379 
380     //--------------------------------------------------------------------------
381     // check the type
382     //--------------------------------------------------------------------------
383 
384     GrB_Info info = GB_Type_check (A->type, "", pr_type, f) ;
385     if (info != GrB_SUCCESS)
386     {
387         GBPR0 ("  %s has an invalid type\n", kind) ;
388         return (GrB_INVALID_OBJECT) ;
389     }
390 
391     //--------------------------------------------------------------------------
392     // report shallow structure
393     //--------------------------------------------------------------------------
394 
395     #if GB_DEVELOPER
396     if (pr_short || pr_complete)
397     {
398         GBPR ("  ->h: %p shallow: %d\n", A->h, A->h_shallow) ;
399         GBPR ("  ->p: %p shallow: %d\n", A->p, A->p_shallow) ;
400         GBPR ("  ->i: %p shallow: %d\n", A->i, A->i_shallow) ;
401         GBPR ("  ->b: %p shallow: %d\n", A->b, A->b_shallow) ;
402         GBPR ("  ->x: %p shallow: %d\n", A->x, A->x_shallow) ;
403     }
404     #endif
405 
406     //--------------------------------------------------------------------------
407     // check p
408     //--------------------------------------------------------------------------
409 
410     if (is_hyper || is_sparse)
411     {
412         if (A->p == NULL)
413         {
414             GBPR0 ("  ->p is NULL, invalid %s\n", kind) ;
415             return (GrB_INVALID_OBJECT) ;
416         }
417     }
418 
419     //--------------------------------------------------------------------------
420     // check a non-empty matrix
421     //--------------------------------------------------------------------------
422 
423     bool A_empty = (A->nzmax == 0) ;
424     if (is_hyper || is_sparse)
425     {
426         if (!A_empty && A->i == NULL)
427         {
428             GBPR0 ("  ->i is NULL, invalid %s\n", kind) ;
429             return (GrB_INVALID_OBJECT) ;
430         }
431     }
432 
433     //--------------------------------------------------------------------------
434     // check the content of p
435     //--------------------------------------------------------------------------
436 
437     if (is_hyper || is_sparse)
438     {
439         if (A->p [0] != 0)
440         {
441             GBPR0 ("  ->p [0] = " GBd " invalid\n", A->p [0]) ;
442             return (GrB_INVALID_OBJECT) ;
443         }
444 
445         for (int64_t j = 0 ; j < A->nvec ; j++)
446         {
447             if (A->p [j+1] < A->p [j] || A->p [j+1] > A->nzmax)
448             {
449                 GBPR0 ("  ->p [" GBd "] = " GBd " invalid\n", j+1, A->p [j+1]) ;
450                 return (GrB_INVALID_OBJECT) ;
451             }
452         }
453     }
454 
455     //--------------------------------------------------------------------------
456     // check the content of h
457     //--------------------------------------------------------------------------
458 
459     if (is_hyper)
460     {
461         int64_t jlast = -1 ;
462         for (int64_t k = 0 ; k < A->nvec ; k++)
463         {
464             int64_t j = A->h [k] ;
465             if (jlast >= j || j < 0 || j >= A->vdim)
466             {
467                 GBPR0 ("  ->h [" GBd "] = " GBd " invalid\n", k, j) ;
468                 return (GrB_INVALID_OBJECT) ;
469             }
470             jlast = j ;
471         }
472     }
473 
474     //--------------------------------------------------------------------------
475     // report name and number of entries
476     //--------------------------------------------------------------------------
477 
478     GBPR0 ("  ") ;
479     if (name != NULL && strlen (name) > 0)
480     {
481         GBPR0 ("%s, ", ((name != NULL) ? name : "")) ;
482     }
483 
484     // # of entries cannot be computed until all the tests above are OK
485     int64_t anz = is_full ? GB_NNZ_FULL (A) : GB_NNZ (A) ;
486     if (anz == 0)
487     {
488         GBPR0 ("no entries\n") ;
489     }
490     else if (anz == 1)
491     {
492         GBPR0 ("1 entry\n") ;
493     }
494     else
495     {
496         GBPR0 ( GBd " entries\n", anz) ;
497     }
498 
499     //--------------------------------------------------------------------------
500     // report the number of pending tuples and zombies
501     //--------------------------------------------------------------------------
502 
503     if (Pending != NULL || A->nzombies != 0)
504     {
505         GBPR0 ("  pending tuples: " GBd " max pending: " GBd
506             " zombies: " GBd "\n", GB_Pending_n (A),
507             (Pending == NULL) ? 0 : (Pending->nmax),
508             A->nzombies) ;
509     }
510 
511     if (!ignore_zombies && (A->nzombies < 0 || A->nzombies > anz))
512     {
513         GBPR0 ("  invalid number of zombies: " GBd " "
514             "must be >= 0 and <= # entries (" GBd ")\n", A->nzombies, anz) ;
515         return (GrB_INVALID_OBJECT) ;
516     }
517 
518     if (is_full || is_bitmap)
519     {
520         if (A->nzombies != 0)
521         {
522             // full/bitmap cannot have zombies
523             GBPR0 ("  %s %s cannot have zombies\n",
524                 is_full ? "full" : "bitmap", kind) ;
525             return (GrB_INVALID_OBJECT) ;
526         }
527         if (Pending != NULL)
528         {
529             // full/bitmap cannot have pending tuples
530             GBPR0 ("  %s %s cannot have pending tuples\n",
531                 is_full ? "full" : "bitmap", kind) ;
532             return (GrB_INVALID_OBJECT) ;
533         }
534         if (A->jumbled)
535         {
536             // full/bitmap jumbled
537             GBPR0 ("  %s %s cannot be jumbled\n",
538                 is_full ? "full" : "bitmap", kind) ;
539             return (GrB_INVALID_OBJECT) ;
540         }
541     }
542 
543     //--------------------------------------------------------------------------
544     // check and print the row indices and numerical values
545     //--------------------------------------------------------------------------
546 
547     if (anz > 0 && !phantom) GBPR0 ("\n") ;
548 
549     #define GB_NBRIEF 10
550     #define GB_NZBRIEF 30
551 
552     int64_t nzombies = 0 ;
553     int64_t icount = 0 ;
554     bool truncated = false ;
555     int64_t anz_actual = 0 ;
556 
557     // for each vector of A
558     for (int64_t k = 0 ; k < A->nvec ; k++)
559     {
560         if (phantom) break ;
561         int64_t ilast = -1 ;
562         int64_t j = GBH (A->h, k) ;
563         int64_t p = GBP (A->p, k, A->vlen) ;
564         int64_t pend = GBP (A->p, k+1, A->vlen) ;
565 
566         // count the entries in A(:,j)
567         int64_t ajnz = pend - p ;
568         if (is_bitmap)
569         {
570             ajnz = 0 ;
571             for (int64_t p2 = p ; p2 < pend ; p2++)
572             {
573                 int8_t ab = A->b [p2] ;
574                 if (ab < 0 || ab > 1)
575                 {
576                     // bitmap with value other than 0, 1
577                     GBPR0 ("    invalid bitmap %d\n", ab) ;
578                     return (GrB_INVALID_OBJECT) ;
579                 }
580                 ajnz += (ab != 0)  ;
581             }
582         }
583 
584         bool prcol = ((pr_short && !truncated) || pr_complete) ;
585         // print the header for vector j
586         if (prcol)
587         {
588             #if GB_DEVELOPER
589             GBPR ("  %s: " GBd " : " GBd " entries [" GBd ":" GBd "]\n",
590                 A->is_csc ? "column" : "row", j, ajnz, p, pend-1) ;
591             if (pr_short && k == GB_NBRIEF) truncated = true ;
592             #endif
593         }
594 
595         // for each entry in A(:,j), the kth vector of A
596         for ( ; p < pend ; p++)
597         {
598             if (!GBB (A->b, p)) continue ;
599             anz_actual++ ;
600             icount++ ;
601 
602             int64_t i = GBI (A->i, p, A->vlen) ;
603             bool is_zombie = GB_IS_ZOMBIE (i) ;
604             i = GB_UNFLIP (i) ;
605             if (is_zombie) nzombies++ ;
606             bool print_value = false ;
607             if (prcol)
608             {
609                 if ((pr_short && icount < GB_NZBRIEF) || pr_complete)
610                 {
611                     print_value = true ;
612                     #if GB_DEVELOPER
613                     GBPR ("    %s " GBd ": ", A->is_csc ? "row":"column", i) ;
614                     #else
615                     if (A->is_csc)
616                     {
617                         GBPR ("    (" GBd "," GBd ") ", i+offset, j+offset) ;
618                     }
619                     else
620                     {
621                         GBPR ("    (" GBd "," GBd ") ", j+offset, i+offset) ;
622                     }
623                     #endif
624                 }
625                 else if (pr_short && (ilast == -1 || icount == GB_NZBRIEF))
626                 {
627                     truncated = true ;
628                 }
629             }
630             int64_t row = A->is_csc ? i : j ;
631             int64_t col = A->is_csc ? j : i ;
632             if (i < 0 || i >= A->vlen)
633             {
634                 GBPR0 ("  index (" GBd "," GBd ") out of range\n",
635                     row+offset, col+offset) ;
636                 return (GrB_INVALID_OBJECT) ;
637             }
638 
639             // print the value
640             if (print_value)
641             {
642                 if (is_zombie)
643                 {
644                     GBPR ("zombie") ;
645                 }
646                 else if (A->x != NULL)
647                 {
648                     GB_void *Ax = (GB_void *) A->x ;
649                     info = GB_entry_check (A->type, Ax +(p * (A->type->size)),
650                         pr, f) ;
651                     if (info != GrB_SUCCESS) return (info) ;
652                 }
653             }
654 
655             // If the matrix is known to be jumbled, then out-of-order
656             // indices are OK (but duplicates are not OK).  If the matrix is
657             // unjumbled, then all indices must appear in ascending order.
658             if (A->jumbled ? (i == ilast) : (i <= ilast))
659             {
660                 // indices unsorted, or duplicates present
661                 GBPR0 (" index (" GBd "," GBd ") invalid\n",
662                     row+offset, col+offset) ;
663                 return (GrB_INDEX_OUT_OF_BOUNDS) ;
664             }
665 
666             if (print_value)
667             {
668                 GBPR ("\n") ;
669             }
670             ilast = i ;
671         }
672     }
673 
674     if (pr_short && truncated) GBPR ("    ...\n") ;
675 
676     //--------------------------------------------------------------------------
677     // check the entry count in the bitmap
678     //--------------------------------------------------------------------------
679 
680     if (is_bitmap && anz != anz_actual)
681     {
682         // bitmap with invalid nvals
683         GBPR0 ("  invalid bitmap count: " GBd " exist but"
684             " A->nvals = " GBd "\n", anz_actual, anz) ;
685         return (GrB_INVALID_OBJECT) ;
686     }
687 
688     //--------------------------------------------------------------------------
689     // check the zombie count
690     //--------------------------------------------------------------------------
691 
692     if (!ignore_zombies && nzombies != A->nzombies)
693     {
694         GBPR0 ("  invalid zombie count: " GBd " exist but"
695             " A->nzombies = " GBd "\n", nzombies, A->nzombies) ;
696         return (GrB_INVALID_OBJECT) ;
697     }
698 
699     //--------------------------------------------------------------------------
700     // check and print the pending tuples
701     //--------------------------------------------------------------------------
702 
703     #if GB_DEVELOPER
704     if ((pr_short || pr_complete) && (is_sparse || is_hyper))
705     {
706         GBPR ("  Pending %p\n", Pending) ;
707     }
708     #endif
709 
710     if (Pending != NULL)
711     {
712 
713         //---------------------------------------------------------------------
714         // A has pending tuples
715         //---------------------------------------------------------------------
716 
717         #if GB_DEVELOPER
718         if (pr_short || pr_complete)
719         {
720             GBPR ("  Pending->i %p\n", Pending->i) ;
721             GBPR ("  Pending->j %p\n", Pending->j) ;
722             GBPR ("  Pending->x %p\n", Pending->x) ;
723         }
724         #endif
725 
726         if (Pending->n < 0 || Pending->n > Pending->nmax ||
727             Pending->nmax < 0)
728         {
729             GBPR0 ("  invalid pending count\n") ;
730             return (GrB_INVALID_OBJECT) ;
731         }
732 
733         // matrix has tuples, arrays and type must not be NULL
734         if (Pending->i == NULL || Pending->x == NULL ||
735             (A->vdim > 1 && Pending->j == NULL))
736         {
737             GBPR0 ("  invalid pending tuples\n") ;
738             return (GrB_INVALID_OBJECT) ;
739         }
740 
741         GBPR0 ("  pending tuples:\n") ;
742 
743         info = GB_Type_check (Pending->type, "", pr, f) ;
744         if (info != GrB_SUCCESS || (Pending->type->size != Pending->size))
745         {
746             // invalid Pending->type
747             GBPR0 ("  %s has an invalid Pending->type\n", kind) ;
748             return (GrB_INVALID_OBJECT) ;
749         }
750 
751         int64_t ilast = -1 ;
752         int64_t jlast = -1 ;
753         bool sorted = true ;
754 
755         for (int64_t k = 0 ; k < Pending->n ; k++)
756         {
757             int64_t i = Pending->i [k] ;
758             int64_t j = (A->vdim <= 1) ? 0 : (Pending->j [k]) ;
759             int64_t row = A->is_csc ? i : j ;
760             int64_t col = A->is_csc ? j : i ;
761 
762             // print the tuple
763             if ((pr_short && k < GB_NZBRIEF) || pr_complete)
764             {
765                 GBPR ("    row: " GBd " col: " GBd " ", row, col) ;
766                 info = GB_entry_check (Pending->type,
767                     Pending->x +(k * Pending->type->size), pr, f) ;
768                 if (info != GrB_SUCCESS) return (info) ;
769                 GBPR ("\n") ;
770             }
771 
772             if (i < 0 || i >= A->vlen || j < 0 || j >= A->vdim)
773             {
774                 GBPR0 ("    tuple (" GBd "," GBd ") out of range\n", row, col) ;
775                 return (GrB_INVALID_OBJECT) ;
776             }
777 
778             sorted = sorted && ((jlast < j) || (jlast == j && ilast <= i)) ;
779             ilast = i ;
780             jlast = j ;
781         }
782 
783         if (sorted != Pending->sorted)
784         {
785             GBPR0 ("  invalid pending tuples: invalid sort\n") ;
786             return (GrB_INVALID_OBJECT) ;
787         }
788 
789         if (Pending->op == NULL)
790         {
791             GBPR0 ("  pending operator: implicit 2nd\n") ;
792         }
793         else
794         {
795             info = GB_BinaryOp_check (Pending->op, "pending operator:", pr, f) ;
796             if (info != GrB_SUCCESS)
797             {
798                 GBPR0 ("  invalid pending operator\n") ;
799                 return (GrB_INVALID_OBJECT) ;
800             }
801         }
802     }
803 
804     if (pr_complete)
805     {
806         GBPR ("\n") ;
807     }
808 
809     //--------------------------------------------------------------------------
810     // check nvec_nonempty
811     //--------------------------------------------------------------------------
812 
813     // A->nvec_nonempty == -1 denotes that the value has not been computed.
814     // This is valid, and can occur for imported matrices and in other cases
815     // when its computation is postponed or not needed.  If not -1, however,
816     // the value must be correct.
817 
818     int64_t actual_nvec_nonempty = GB_nvec_nonempty (A, NULL) ;
819 
820     if (! ((A->nvec_nonempty == actual_nvec_nonempty) ||
821            (A->nvec_nonempty == -1)))
822     {
823         // invalid nvec_nonempty
824         GBPR0 ("  invalid count of non-empty vectors\n") ;
825         return (GrB_INVALID_OBJECT) ;
826     }
827 
828     //--------------------------------------------------------------------------
829     // return result
830     //--------------------------------------------------------------------------
831 
832     return (GrB_SUCCESS) ;
833 }
834 
835