1#ifndef TEST_UTILITIES_INC
2#define TEST_UTILITIES_INC
3
4/*
5 * This file contains functions that are useful when writing tests.
6 * Include it in the test program using #include "test_utilities.inc"
7 */
8
9#include <igraph.h>
10#include <stdio.h>
11#include <string.h>
12
13/* Print an igraph_real_t value. Be consistent in printing NaN/Inf across platforms. */
14void print_real(FILE *f, igraph_real_t x, const char *format) {
15    igraph_bool_t g8 = !strcmp(format, "%8g");
16    if (igraph_finite(x)) {
17        if (x == 0 && signbit(x)) {
18            /* print negative zeros as positive zeros for sake of consistency */
19            x = +0.0;
20        }
21        fprintf(f, format, x);
22    } else if (igraph_is_nan(x)) {
23        fprintf(f, g8 ? "     NaN" : "NaN");
24    } else if (igraph_is_posinf(x)) {
25        fprintf(f, g8 ? "     Inf" : "Inf");
26    } else if (igraph_is_neginf(x)) {
27        fprintf(f, g8 ? "    -Inf" : "-Inf");
28    }
29}
30
31void print_vector_format(const igraph_vector_t *v, FILE *f, const char *format) {
32    long i, n = igraph_vector_size(v);
33    fprintf(f, "(");
34    for (i=0; i < n; i++) {
35        fprintf(f, " ");
36        print_real(f, VECTOR(*v)[i], format);
37    }
38    fprintf(f, " )\n");
39}
40
41/* Print elements of a vector. Use parentheses to make it clear when a vector has size zero. */
42void print_vector(const igraph_vector_t *v) {
43    print_vector_format(v, stdout, "%g");
44}
45
46/* Round elements of a vector to integers and print them. */
47/* This is meant to be used when the elements of a vector are integer values. */
48void print_vector_round(const igraph_vector_t *v) {
49    print_vector_format(v, stdout, "%.f");
50}
51
52
53/* Print elements of an integer vector */
54void print_vector_int(const igraph_vector_int_t *v) {
55    long i, n = igraph_vector_int_size(v);
56    printf("(");
57    for (i=0; i < n; i++) {
58        printf(" %" IGRAPH_PRId, VECTOR(*v)[i]);
59    }
60    printf(" )\n");
61}
62
63
64/* Print elements of a long vector */
65void print_vector_long(const igraph_vector_long_t *v) {
66    long i, n = igraph_vector_long_size(v);
67    printf("(");
68    for (i=0; i < n; i++) {
69        printf(" %ld", VECTOR(*v)[i]);
70    }
71    printf(" )\n");
72}
73
74
75/* Print elements of a matrix. Use brackets to make it clear when a vector has size zero. */
76void print_matrix_format(const igraph_matrix_t *m, FILE *f, const char *format) {
77    long i, j, nrow = igraph_matrix_nrow(m), ncol = igraph_matrix_ncol(m);
78    for (i = 0; i < nrow; i++) {
79        fprintf(f, i == 0 ? "[" : " ");
80        for (j = 0; j < ncol; j++) {
81            fprintf(f, " ");
82            print_real(f, MATRIX(*m, i, j), format);
83        }
84        fprintf(f, i == nrow-1 ? " ]\n" : "\n");
85    }
86}
87
88void print_matrix(const igraph_matrix_t *m) {
89    print_matrix_format(m, stdout, "%8g");
90}
91
92/* Round elements of a matrix to integers and print them. */
93/* This is meant to be used when the elements of a matrix are integer values. */
94void print_matrix_round(const igraph_matrix_t *m) {
95    print_matrix_format(m, stdout, "%4.f");
96}
97
98
99/* Print an adjacency list. Use brackets around each vector and also use
100 * brackets around the entire adjacency list to make it clear when the list
101 * is empty.
102 */
103void print_adjlist(const igraph_adjlist_t *adjlist) {
104    long vcount = igraph_adjlist_size(adjlist);
105    long i;
106
107    printf("{\n");
108    for (i = 0; i < vcount; ++i) {
109        printf("  %ld: ", i);
110        print_vector_int(igraph_adjlist_get(adjlist, i));
111    }
112    printf("}\n");
113}
114
115/* Print a graph. Use brackets to make it obvious when the edge list is empty. */
116void print_graph(const igraph_t *graph) {
117    long ecount = igraph_ecount(graph);
118    long vcount = igraph_vcount(graph);
119    long i;
120
121    printf("directed: %s\n", igraph_is_directed(graph) ? "true" : "false");
122    printf("vcount: %ld\n", vcount);
123    printf("edges: {\n");
124    for (i=0; i < ecount; ++i)
125        printf("%" IGRAPH_PRId " %" IGRAPH_PRId "\n", IGRAPH_FROM(graph, i), IGRAPH_TO(graph, i));
126    printf("}\n");
127}
128
129/* Print an incidence list. Use brackets around each vector and also use
130 * brackets around the entire incidence list to make it clear when the list
131 * is empty.
132 */
133void print_inclist(const igraph_inclist_t *inclist) {
134    long vcount = igraph_inclist_size(inclist);
135    long i;
136
137    printf("{\n");
138    for (i = 0; i < vcount; ++i) {
139        printf("  %ld: ", i);
140        print_vector_int(igraph_inclist_get(inclist, i));
141    }
142    printf("}\n");
143}
144
145/* Print a lazy adjacency list. Use brackets around each vector and also use
146 * brackets around the entire lazy adjacency list to make it clear when the list
147 * is empty.
148 */
149void print_lazy_adjlist(igraph_lazy_adjlist_t *adjlist) {
150    long vcount = igraph_lazy_adjlist_size(adjlist);
151    long i;
152
153    printf("{\n");
154    for (i = 0; i < vcount; ++i) {
155        printf("  %ld: ", i);
156        print_vector_int(igraph_lazy_adjlist_get(adjlist, i));
157    }
158    printf("}\n");
159}
160
161/* Print a lazy incidence list. Use brackets around each vector and also use
162 * brackets around the entire incidence list to make it clear when the list
163 * is empty.
164 */
165void print_lazy_inclist(igraph_lazy_inclist_t *inclist) {
166    long vcount = igraph_lazy_inclist_size(inclist);
167    long i;
168
169    printf("{\n");
170    for (i = 0; i < vcount; ++i) {
171        printf("  %ld: ", i);
172        print_vector_int(igraph_lazy_inclist_get(inclist, i));
173    }
174    printf("}\n");
175}
176
177/* Edge comparisong function used for sorting in print_graph_canon(). */
178int edge_compare(const void *e1, const void *e2) {
179    const igraph_real_t *edge1 = (igraph_real_t *) e1, *edge2 = (igraph_real_t *) e2;
180    if (edge1[0] < edge2[0]) {
181        return -1;
182    } else if (edge1[0] > edge2[0]) {
183        return 1;
184    } else if (edge1[1] < edge2[1]) {
185        return -1;
186    } else if (edge1[1] > edge2[1]) {
187        return 1;
188    } else {
189        return 0;
190    }
191}
192
193/* Print a graph using a sorted edge list. Other than sorting (i.e. canonicalizing) the edge list,
194 * this function is identical to print_graph(). */
195void print_graph_canon(const igraph_t *graph) {
196    long ecount = igraph_ecount(graph);
197    long vcount = igraph_vcount(graph);
198    long i;
199    igraph_vector_t edges;
200
201    printf("directed: %s\n", igraph_is_directed(graph) ? "true" : "false");
202    printf("vcount: %ld\n", vcount);
203    printf("edges: {\n");
204
205    igraph_vector_init(&edges, 0);
206    igraph_get_edgelist(graph, &edges, 0);
207
208    /* If the graph is undirected, we make sure that the first vertex of undirected edges
209     * is always the one with the lower ID. */
210    if (! igraph_is_directed(graph)) {
211        for (i=0; i < ecount; ++i) {
212            if (VECTOR(edges)[2*i] > VECTOR(edges)[2*i+1]) {
213                igraph_real_t tmp = VECTOR(edges)[2*i];
214                VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1];
215                VECTOR(edges)[2*i+1] = tmp;
216            }
217        }
218    }
219
220    /* Sort the edge list */
221    igraph_qsort(&VECTOR(edges)[0], ecount, 2*sizeof(igraph_real_t), &edge_compare);
222
223    for (i=0; i < ecount; ++i) {
224        printf("%ld %ld\n", (long) VECTOR(edges)[2*i], (long) VECTOR(edges)[2*i+1]);
225    }
226
227    igraph_vector_destroy(&edges);
228
229    printf("}\n");
230}
231
232/* Print a vector, ensuring that the first nonzero element is positive. */
233void print_vector_first_nonzero_element_positive(const igraph_vector_t *vector, const char* format) {
234    igraph_vector_t copy;
235    long i, n;
236
237    igraph_vector_copy(&copy, vector);
238
239    n = igraph_vector_size(&copy);
240
241    for (i = 0; i < n; i++) {
242        if (VECTOR(copy)[i] < 0) {
243            for (; i < n; i++) {
244                if (VECTOR(copy)[i] != 0) {
245                    VECTOR(copy)[i] *= -1;
246                }
247            }
248            break;
249        } else if (VECTOR(copy)[i] > 0) {
250            break;
251        }
252    }
253
254    igraph_vector_printf(&copy, format);
255    igraph_vector_destroy(&copy);
256}
257
258/* Print a complex vector, ensuring that the first element with nonzero real
259 * part has a positive real part. */
260void print_vector_complex_first_nonzero_real_part_positive(const igraph_vector_complex_t *vector) {
261    igraph_vector_complex_t copy;
262    long i, n;
263
264    igraph_vector_complex_copy(&copy, vector);
265
266    n = igraph_vector_complex_size(&copy);
267
268    for (i = 0; i < n; i++) {
269        if (IGRAPH_REAL(VECTOR(copy)[i]) < 0) {
270            for (; i < n; i++) {
271                if (IGRAPH_REAL(VECTOR(copy)[i]) != 0) {
272                    IGRAPH_REAL(VECTOR(copy)[i]) *= -1;
273                }
274                if (IGRAPH_IMAG(VECTOR(copy)[i]) != 0) {
275                    IGRAPH_IMAG(VECTOR(copy)[i]) *= -1;
276                }
277            }
278            break;
279        } else if (IGRAPH_REAL(VECTOR(copy)[i]) > 0) {
280            break;
281        }
282    }
283
284    igraph_vector_complex_print(&copy);
285    igraph_vector_complex_destroy(&copy);
286}
287
288/* Print a matrix, ensuring that the first nonzero element in each column is
289 * positive. */
290void print_matrix_first_row_positive(const igraph_matrix_t *matrix, const char* format) {
291    igraph_matrix_t copy;
292    long i, j, nrow, ncol;
293
294    igraph_matrix_copy(&copy, matrix);
295
296    nrow = igraph_matrix_nrow(&copy);
297    ncol = igraph_matrix_ncol(&copy);
298
299    for (i = 0; i < ncol; i++) {
300        for (j = 0; j < nrow; j++) {
301            if (MATRIX(copy, j, i) < 0) {
302                for (; j < nrow; j++) {
303                    if (MATRIX(copy, j, i) != 0) {
304                        MATRIX(copy, j, i) *= -1;
305                    }
306                }
307                break;
308            } else if (MATRIX(copy, j, i) > 0) {
309                break;
310            }
311        }
312    }
313
314    igraph_matrix_printf(&copy, format);
315    igraph_matrix_destroy(&copy);
316}
317
318/* Print a complex matrix, ensuring that the first element with nonzero real
319 * part in each column has a positive real part. */
320void print_matrix_complex_first_row_positive(const igraph_matrix_complex_t *matrix) {
321    igraph_matrix_complex_t copy;
322    long i, j, nrow, ncol;
323    igraph_complex_t z;
324    char buf[256];
325    size_t len;
326
327    igraph_matrix_complex_copy(&copy, matrix);
328
329    nrow = igraph_matrix_complex_nrow(&copy);
330    ncol = igraph_matrix_complex_ncol(&copy);
331
332    for (i = 0; i < ncol; i++) {
333        for (j = 0; j < nrow; j++) {
334            if (IGRAPH_REAL(MATRIX(copy, j, i)) < 0) {
335                for (; j < nrow; j++) {
336                    if (IGRAPH_REAL(MATRIX(copy, j, i)) != 0) {
337                        IGRAPH_REAL(MATRIX(copy, j, i)) *= -1;
338                    }
339                    if (IGRAPH_IMAG(MATRIX(copy, j, i)) != 0) {
340                        IGRAPH_IMAG(MATRIX(copy, j, i)) *= -1;
341                    }
342                }
343                break;
344            } else if (IGRAPH_REAL(MATRIX(copy, j, i)) > 0) {
345                break;
346            }
347        }
348    }
349
350    for (i = 0; i < nrow; i++) {
351        for (j = 0; j < ncol; j++) {
352            z = MATRIX(copy, i, j);
353            if (j != 0) {
354                putchar(' ');
355            }
356
357            snprintf(buf, sizeof(buf), "%g%+gi", IGRAPH_REAL(z), IGRAPH_IMAG(z));
358            len = strlen(buf);
359
360            /* ensure that we don't print -0 in the imaginary part */
361            if (len > 3 && buf[len-3] == '-' && buf[len-2] == '0' && buf[len-1] == 'i') {
362              buf[len-3] = '+';
363            }
364
365            /* ensure that we don't print -0 in the real part either */
366            if (buf[0] == '-' && buf[1] == '0' && (buf[2] == '+' || buf[2] == '-')) {
367                printf("%s", buf + 1);
368            } else {
369                printf("%s", buf);
370            }
371        }
372        printf("\n");
373    }
374
375    igraph_matrix_complex_destroy(&copy);
376}
377
378void matrix_init_int_row_major(igraph_matrix_t *mat, int nrow, int ncol, int* elem) {
379    int c, r;
380    int i_elem = 0;
381    igraph_matrix_init(mat, nrow, ncol);
382    for (r = 0; r < nrow; r++) {
383        for (c = 0; c < ncol; c++) {
384            MATRIX(*mat, r, c) = elem[i_elem];
385            i_elem++;
386        }
387    }
388}
389
390void matrix_init_real_row_major(igraph_matrix_t *mat, int nrow, int ncol, igraph_real_t* elem) {
391    int c, r;
392    int i_elem = 0;
393    igraph_matrix_init(mat, nrow, ncol);
394    for (r = 0; r < nrow; r++) {
395        for (c = 0; c < ncol; c++) {
396            MATRIX(*mat, r, c) = elem[i_elem];
397            i_elem++;
398        }
399    }
400}
401
402void matrix_chop(igraph_matrix_t *mat, igraph_real_t cutoff) {
403    int i;
404    for (i = 0; i < igraph_matrix_nrow(mat) * igraph_matrix_ncol(mat); i++) {
405        if (fabs(VECTOR(mat->data)[i]) < cutoff) {
406            VECTOR(mat->data)[i] = 0;
407        }
408    }
409}
410
411void print_spmatrix(igraph_spmatrix_t *m) {
412    long int i, j;
413    for (i = 0; i < igraph_spmatrix_nrow(m); i++) {
414        for (j = 0; j < igraph_spmatrix_ncol(m); j++) {
415            printf(" %8g", igraph_spmatrix_e(m, i, j));
416        }
417        printf("\n");
418    }
419}
420
421#define VERIFY_FINALLY_STACK() \
422    if (!IGRAPH_FINALLY_STACK_EMPTY) { \
423        printf( \
424          "%s:%d : " \
425          "Finally stack is not empty (stack size is %d). " \
426          "Check that the number in IGRAPH_FINALLY_CLEAN matches the IGRAPH_FINALLY count.\n", \
427          IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_FINALLY_STACK_SIZE()); \
428        abort(); \
429    }
430
431/* Run a test in a separate function; return the return value of the function
432 * if it is nonzero. Also verify the FINALLY stack and bail out if it is not
433 * empty. Needs an integer variable named 'retval' in the local context. */
434#define RUN_TEST(func) \
435    { \
436        retval = func(); \
437        if (retval) { \
438            return retval; \
439        } \
440        VERIFY_FINALLY_STACK(); \
441    }
442
443#define CHECK_ERROR(funcall, expected_err) \
444    do { \
445        igraph_error_handler_t *handler; \
446        handler = igraph_set_error_handler(igraph_error_handler_ignore); \
447        IGRAPH_ASSERT(funcall == expected_err); \
448        igraph_set_error_handler(handler); \
449    } while (0)
450
451#endif /* TEST_UTILITIES_INC */
452