Coverage Report

Created: 2025-11-10 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/graph/cattributes.c
Line
Count
Source
1
/*
2
   igraph library.
3
   Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
4
   334 Harvard street, Cambridge, MA 02139 USA
5
6
   This program is free software; you can redistribute it and/or modify
7
   it under the terms of the GNU General Public License as published by
8
   the Free Software Foundation; either version 2 of the License, or
9
   (at your option) any later version.
10
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
   GNU General Public License for more details.
14
15
   You should have received a copy of the GNU General Public License
16
   along with this program; if not, write to the Free Software
17
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18
   02110-1301 USA
19
20
*/
21
22
#include "igraph_attributes.h"
23
#include "igraph_memory.h"
24
#include "igraph_interface.h"
25
#include "igraph_random.h"
26
27
#include <string.h>
28
29
/* An attribute is either a numeric vector (vector_t), a boolean vector
30
 * (vector_bool_t) or a string vector (strvector_t).
31
 * The attribute itself is stored in a struct igraph_attribute_record_t. There
32
 * is one such object for each attribute. The igraph_t has a pointer to an
33
 * igraph_i_cattribute_t, which contains three igraph_attribute_vector_list_t's,
34
 * each holding pointers to igraph_attribute_record_t objects. */
35
36
typedef struct igraph_i_cattributes_t {
37
    igraph_attribute_record_list_t gal;
38
    igraph_attribute_record_list_t val;
39
    igraph_attribute_record_list_t eal;
40
} igraph_i_cattributes_t;
41
42
/*
43
 * Finds an attribute with the given name in an attribute record list, and
44
 * returns the index of the record in the list, or -1 if there was no such
45
 * attribute.
46
 */
47
static igraph_int_t igraph_i_cattribute_find_index(
48
    const igraph_attribute_record_list_t *attrs, const char *name
49
31.1M
) {
50
31.1M
    igraph_int_t n = igraph_attribute_record_list_size(attrs);
51
38.3M
    for (igraph_int_t i = 0; i < n; i++) {
52
38.2M
        const igraph_attribute_record_t *rec = igraph_attribute_record_list_get_ptr(attrs, i);
53
38.2M
        if (!strcmp(rec->name, name)) {
54
31.0M
            return i;
55
31.0M
        }
56
38.2M
    }
57
123k
    return -1;
58
31.1M
}
59
60
/*
61
 * Finds an attribute with the given name in an attribute record list, and
62
 * returns the attribute record or NULL if there was no such attribute.
63
 * Optionally, the type of the attribute can be enforced; use
64
 * IGRAPH_ATTRIBUTE_UNSPECIFIED if you do not care about the type.
65
 */
66
static igraph_attribute_record_t* igraph_i_cattribute_find(
67
    igraph_attribute_record_list_t *attrs, const char *name,
68
    igraph_attribute_type_t type
69
31.0M
) {
70
31.0M
    igraph_int_t index = igraph_i_cattribute_find_index(attrs, name);
71
31.0M
    igraph_attribute_record_t *rec;
72
73
31.0M
    if (index >= 0) {
74
31.0M
        rec = igraph_attribute_record_list_get_ptr(attrs, index);
75
31.0M
        if (type == IGRAPH_ATTRIBUTE_UNSPECIFIED || type == rec->type) {
76
31.0M
            return rec;
77
31.0M
        }
78
31.0M
    }
79
80
6.91k
    return NULL;
81
31.0M
}
82
83
/*
84
 * Same as igraph_i_cattribute_find(), but suitable for const attribute record
85
 * lists.
86
 */
87
static const igraph_attribute_record_t* igraph_i_cattribute_find_const(
88
    const igraph_attribute_record_list_t *attrs, const char *name,
89
    igraph_attribute_type_t type
90
6.60k
) {
91
6.60k
    igraph_int_t index = igraph_i_cattribute_find_index(attrs, name);
92
6.60k
    const igraph_attribute_record_t *rec;
93
94
6.60k
    if (index >= 0) {
95
6.60k
        rec = igraph_attribute_record_list_get_ptr(attrs, index);
96
6.60k
        if (type == IGRAPH_ATTRIBUTE_UNSPECIFIED || type == rec->type) {
97
6.60k
            return rec;
98
6.60k
        }
99
6.60k
    }
100
101
0
    return NULL;
102
6.60k
}
103
104
/*
105
 * Finds an attribute with the given name in an attribute record list, and
106
 * returns the attribute record in an output argument. Returns an error code
107
 * if there is no such attribute. Optionally, the type of the attribute can be
108
 * enforced; use IGRAPH_ATTRIBUTE_UNSPECIFIED if you do not care about the type.
109
 */
110
static igraph_error_t igraph_i_cattribute_find_or_return(
111
    igraph_attribute_record_list_t *attrs, const char *name,
112
    igraph_attribute_type_t type, igraph_attribute_record_t **ptr
113
31.0M
) {
114
31.0M
    igraph_attribute_record_t *rec;
115
116
31.0M
    rec = igraph_i_cattribute_find(attrs, name, IGRAPH_ATTRIBUTE_UNSPECIFIED);
117
31.0M
    if (!rec) {
118
0
        IGRAPH_ERRORF("Attribute '%s' does not exist.", IGRAPH_EINVAL, name);
119
0
    }
120
121
31.0M
    if (type != IGRAPH_ATTRIBUTE_UNSPECIFIED) {
122
30.9M
        IGRAPH_CHECK(igraph_attribute_record_check_type(rec, type));
123
30.9M
    }
124
125
31.0M
    if (ptr) {
126
31.0M
        *ptr = rec;
127
31.0M
    }
128
129
31.0M
    return IGRAPH_SUCCESS;
130
31.0M
}
131
132
/*
133
 * Finds an attribute with the given name in an attribute record list, and
134
 * returns the attribute record in an output argument. Creates a new attribute
135
 * with the given name if there is no such attribute. The type of the attribute
136
 * needs to be specified so we can create the appropriate value vector if needed.
137
 * You can specify a length; when the existing value vector for the attribute
138
 * is shorter than this length, it will be extended to ensure that it has at
139
 * least this many elements. You can pass 0 as the length if you do not want to
140
 * expand value vectors.
141
 */
142
static igraph_error_t igraph_i_cattribute_find_or_create(
143
    igraph_attribute_record_list_t *attrs,
144
    const char *name, igraph_attribute_type_t type,
145
    igraph_int_t length,
146
    igraph_attribute_record_t **ptr
147
6.91k
) {
148
6.91k
    igraph_attribute_record_t *rec;
149
150
6.91k
    rec = igraph_i_cattribute_find(attrs, name, IGRAPH_ATTRIBUTE_UNSPECIFIED);
151
6.91k
    if (rec) {
152
0
        if (type != IGRAPH_ATTRIBUTE_UNSPECIFIED) {
153
0
            IGRAPH_CHECK(igraph_attribute_record_check_type(rec, type));
154
0
        }
155
6.91k
    } else {
156
6.91k
        IGRAPH_CHECK(igraph_attribute_record_list_push_back_new(attrs, &rec));
157
6.91k
        IGRAPH_CHECK(igraph_attribute_record_set_name(rec, name));
158
6.91k
        IGRAPH_CHECK(igraph_attribute_record_set_type(rec, type));
159
6.91k
    }
160
161
6.91k
    if (length > 0 && igraph_attribute_record_size(rec) < length) {
162
0
        IGRAPH_CHECK(igraph_attribute_record_resize(rec, length));
163
0
    }
164
165
6.91k
    if (ptr) {
166
0
        *ptr = rec;
167
0
    }
168
169
6.91k
    return IGRAPH_SUCCESS;
170
6.91k
}
171
172
/*
173
 * Restores attribute vector lengths to their original size after a failure.
174
 * This function assumes that none of the attribute vectors are shorter than origlen.
175
 * Some may be longer due to a partially completed size extension: these will be
176
 * shrunk to their original size.
177
 */
178
static void igraph_i_cattribute_revert_attribute_vector_sizes(
179
91
        igraph_attribute_record_list_t *attrlist, igraph_int_t origlen) {
180
181
91
    igraph_int_t no_of_attrs = igraph_attribute_record_list_size(attrlist);
182
488
    for (igraph_int_t i = 0; i < no_of_attrs; i++) {
183
397
        igraph_attribute_record_t *rec = igraph_attribute_record_list_get_ptr(attrlist, i);
184
397
        IGRAPH_ASSERT(igraph_attribute_record_size(rec) >= origlen);
185
397
        if (igraph_attribute_record_resize(rec, origlen) != IGRAPH_SUCCESS) {
186
            /* We should have succeeded for known attribute types because we
187
             * always shrink the vector so throw a fatal error if this happens */
188
0
            IGRAPH_FATAL("Unknown attribute type encountered.");
189
0
        }
190
397
    }
191
91
}
192
193
static igraph_error_t igraph_i_cattribute_init(
194
    igraph_t *graph, const igraph_attribute_record_list_t *attr
195
2.27k
) {
196
2.27k
    igraph_i_cattributes_t *nattr;
197
198
2.27k
    nattr = IGRAPH_CALLOC(1, igraph_i_cattributes_t);
199
2.27k
    IGRAPH_CHECK_OOM(nattr, "Insufficient memory to allocate attribute storage.");
200
2.27k
    IGRAPH_FINALLY(igraph_free, nattr);
201
202
2.27k
    if (attr) {
203
0
        IGRAPH_CHECK(igraph_attribute_record_list_init_copy(&nattr->gal, attr));
204
2.27k
    } else {
205
2.27k
        IGRAPH_CHECK(igraph_attribute_record_list_init(&nattr->gal, 0));
206
2.27k
    }
207
2.27k
    IGRAPH_FINALLY(igraph_attribute_record_list_destroy, &nattr->gal);
208
209
2.27k
    IGRAPH_CHECK(igraph_attribute_record_list_init(&nattr->val, 0));
210
2.27k
    IGRAPH_FINALLY(igraph_attribute_record_list_destroy, &nattr->val);
211
212
2.27k
    IGRAPH_CHECK(igraph_attribute_record_list_init(&nattr->eal, 0));
213
2.27k
    IGRAPH_FINALLY(igraph_attribute_record_list_destroy, &nattr->eal);
214
215
2.27k
    graph->attr = nattr;
216
2.27k
    IGRAPH_FINALLY_CLEAN(4);
217
218
2.27k
    return IGRAPH_SUCCESS;
219
2.27k
}
220
221
2.27k
static void igraph_i_cattribute_destroy(igraph_t *graph) {
222
2.27k
    igraph_i_cattributes_t *attr = graph->attr;
223
2.27k
    igraph_attribute_record_list_destroy(&attr->eal);
224
2.27k
    igraph_attribute_record_list_destroy(&attr->val);
225
2.27k
    igraph_attribute_record_list_destroy(&attr->gal);
226
2.27k
    IGRAPH_FREE(graph->attr); /* sets to NULL */
227
2.27k
}
228
229
/* No reference counting here. If you use attributes in C you should
230
   know what you're doing. */
231
232
static igraph_error_t igraph_i_cattribute_copy(
233
    igraph_t *to, const igraph_t *from,
234
    igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea
235
0
) {
236
0
    igraph_i_cattributes_t *attrto, *attrfrom = from->attr;
237
0
    igraph_attribute_record_list_t *alto[3], *alfrom[3] = {
238
0
        &attrfrom->gal, &attrfrom->val, &attrfrom->eal
239
0
    };
240
0
    igraph_bool_t copy[3] = { ga, va, ea };
241
242
0
    attrto = IGRAPH_CALLOC(1, igraph_i_cattributes_t);
243
0
    IGRAPH_CHECK_OOM(attrto, "Insufficient memory to copy attributes.");
244
0
    IGRAPH_FINALLY(igraph_free, attrto);
245
246
0
    alto[0] = &attrto->gal;
247
0
    alto[1] = &attrto->val;
248
0
    alto[2] = &attrto->eal;
249
250
0
    for (igraph_int_t i = 0; i < 3; i++) {
251
0
        if (copy[i]) {
252
0
            IGRAPH_CHECK(igraph_attribute_record_list_init_copy(alto[i], alfrom[i]));
253
0
        } else {
254
0
            IGRAPH_CHECK(igraph_attribute_record_list_init(alto[i], 0));
255
0
        }
256
0
        IGRAPH_FINALLY(igraph_attribute_record_list_destroy, alto[i]);
257
0
    }
258
259
0
    to->attr = attrto;
260
0
    IGRAPH_FINALLY_CLEAN(4);
261
262
0
    return IGRAPH_SUCCESS;
263
0
}
264
265
static igraph_error_t igraph_i_cattribute_add_vertices_or_edges_inner(
266
    igraph_attribute_record_list_t *val,
267
    igraph_int_t newlen, igraph_int_t nv,
268
    const igraph_attribute_record_list_t *nattr
269
6.77k
) {
270
6.77k
    igraph_int_t length;
271
6.77k
    igraph_int_t nattrno = nattr == NULL ? 0 : igraph_attribute_record_list_size(nattr);
272
6.77k
    igraph_int_t origlen = newlen - nv;
273
274
6.77k
    IGRAPH_ASSERT(origlen >= 0);
275
276
    /* Find all the attributes that are newly added, and create new value vectors
277
     * for them in the original graph */
278
13.6k
    for (igraph_int_t i = 0; i < nattrno; i++) {
279
6.91k
        const igraph_attribute_record_t *nattr_entry = igraph_attribute_record_list_get_ptr(nattr, i);
280
6.91k
        const char *nname = nattr_entry->name;
281
6.91k
        IGRAPH_CHECK(igraph_i_cattribute_find_or_create(val, nname, nattr_entry->type, origlen, NULL));
282
6.91k
    }
283
284
    /* Now append the new values */
285
6.77k
    length = igraph_attribute_record_list_size(val);
286
13.2k
    for (igraph_int_t i = 0; i < length; i++) {
287
6.60k
        igraph_attribute_record_t *oldrec = igraph_attribute_record_list_get_ptr(val, i);
288
6.60k
        const igraph_attribute_record_t *newrec = nattr
289
6.60k
            ? igraph_i_cattribute_find_const(nattr, oldrec->name, oldrec->type)
290
6.60k
            : NULL;
291
292
6.60k
        IGRAPH_ASSERT(igraph_attribute_record_size(oldrec) == origlen);
293
294
6.60k
        if (newrec) {
295
            /* This attribute is present in nattr */
296
6.60k
            switch (oldrec->type) {
297
1.68k
            case IGRAPH_ATTRIBUTE_NUMERIC:
298
1.68k
                if (nv != igraph_vector_size(newrec->value.as_vector)) {
299
47
                    IGRAPH_ERROR("Invalid numeric attribute length.", IGRAPH_EINVAL);
300
47
                }
301
1.63k
                IGRAPH_CHECK(igraph_vector_append(
302
1.63k
                    oldrec->value.as_vector, newrec->value.as_vector
303
1.63k
                ));
304
1.63k
                break;
305
4.55k
            case IGRAPH_ATTRIBUTE_STRING:
306
4.55k
                if (nv != igraph_strvector_size(newrec->value.as_strvector)) {
307
44
                    IGRAPH_ERROR("Invalid string attribute length.", IGRAPH_EINVAL);
308
44
                }
309
4.51k
                IGRAPH_CHECK(igraph_strvector_append(
310
4.51k
                    oldrec->value.as_strvector, newrec->value.as_strvector
311
4.51k
                ));
312
4.51k
                break;
313
4.51k
            case IGRAPH_ATTRIBUTE_BOOLEAN:
314
364
                if (nv != igraph_vector_bool_size(newrec->value.as_vector_bool)) {
315
0
                    IGRAPH_ERROR("Invalid boolean attribute length.", IGRAPH_EINVAL);
316
0
                }
317
364
                IGRAPH_CHECK(igraph_vector_bool_append(
318
364
                    oldrec->value.as_vector_bool, newrec->value.as_vector_bool
319
364
                ));
320
364
                break;
321
364
            default:
322
0
                IGRAPH_WARNINGF(
323
0
                    "Attribute '%s' with unknown type %d ignored",
324
0
                    oldrec->name, (int) oldrec->type
325
0
                );
326
0
                break;
327
6.60k
            }
328
6.60k
        } else {
329
            /* No such attribute among the new ones so just extend the length
330
             * of the current record */
331
0
            IGRAPH_CHECK(igraph_attribute_record_resize(oldrec, newlen));
332
0
        }
333
334
6.51k
        IGRAPH_ASSERT(igraph_attribute_record_size(oldrec) == newlen);
335
6.51k
    }
336
337
6.67k
    return IGRAPH_SUCCESS;
338
6.77k
}
339
340
static igraph_error_t igraph_i_cattribute_add_vertices_or_edges(
341
    igraph_attribute_record_list_t *val,
342
    igraph_int_t newlen, igraph_int_t nv,
343
    const igraph_attribute_record_list_t *nattr
344
6.77k
) {
345
6.77k
    igraph_int_t origlen = newlen - nv;
346
6.77k
    igraph_error_t err = igraph_i_cattribute_add_vertices_or_edges_inner(
347
6.77k
        val, newlen, nv, nattr
348
6.77k
    );
349
350
6.77k
    if (err != IGRAPH_SUCCESS) {
351
        /* If unsuccessful, revert attribute vector sizes.
352
         * The following function assumes that all attributes vectors that
353
         * are present have a length at least as great as origlen.
354
         * This is true at the moment because any new attributes that are
355
         * added to the graph are created directly at 'origlen' instead of
356
         * being created at smaller sizes and resized later.
357
         *
358
         * NOTE: While this ensures that all attribute vector lengths are
359
         * correct, it does not ensure that no extra attributes have
360
         * been added to the graph. However, the presence of extra
361
         * attributes does not make the attribute table inconsistent
362
         * like the incorrect attribute vector lengths would.
363
         */
364
91
        igraph_i_cattribute_revert_attribute_vector_sizes(val, origlen);
365
91
    }
366
367
6.77k
    return err;
368
6.77k
}
369
370
static igraph_error_t igraph_i_cattribute_add_vertices(
371
    igraph_t *graph, igraph_int_t nv,
372
    const igraph_attribute_record_list_t *nattr
373
4.55k
) {
374
4.55k
    igraph_i_cattributes_t *attr = graph->attr;
375
4.55k
    return igraph_i_cattribute_add_vertices_or_edges(&attr->val, igraph_vcount(graph), nv, nattr);
376
4.55k
}
377
378
typedef struct {
379
    igraph_vector_t *numeric;
380
    igraph_vector_bool_t *boolean;
381
    igraph_vector_ptr_t *strings;
382
    igraph_int_t length;
383
} igraph_i_attribute_permutation_work_area_t;
384
385
static igraph_error_t igraph_i_attribute_permutation_work_area_init(
386
  igraph_i_attribute_permutation_work_area_t *work_area, igraph_int_t length
387
0
) {
388
0
    work_area->length = length;
389
0
    work_area->numeric = NULL;
390
0
    work_area->boolean = NULL;
391
0
    work_area->strings = NULL;
392
0
    return IGRAPH_SUCCESS;
393
0
}
394
395
static void igraph_i_attribute_permutation_work_area_release_stored_strvectors(
396
  igraph_i_attribute_permutation_work_area_t *work_area
397
0
) {
398
0
    if (work_area->strings != NULL) {
399
0
        igraph_vector_ptr_destroy_all(work_area->strings);
400
0
        IGRAPH_FREE(work_area->strings);
401
0
        work_area->strings = NULL;
402
0
    }
403
0
}
404
405
static void igraph_i_attribute_permutation_work_area_destroy(
406
  igraph_i_attribute_permutation_work_area_t *work_area
407
0
) {
408
0
    igraph_i_attribute_permutation_work_area_release_stored_strvectors(work_area);
409
0
    if (work_area->numeric != NULL) {
410
0
        igraph_vector_destroy(work_area->numeric);
411
0
        IGRAPH_FREE(work_area->numeric);
412
0
        work_area->numeric = NULL;
413
0
    }
414
0
    if (work_area->boolean != NULL) {
415
0
        igraph_vector_bool_destroy(work_area->boolean);
416
0
        IGRAPH_FREE(work_area->boolean);
417
0
        work_area->boolean = NULL;
418
0
    }
419
0
}
420
421
static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_numeric(
422
  igraph_i_attribute_permutation_work_area_t *work_area
423
0
) {
424
0
    igraph_vector_t* vec = work_area->numeric;
425
426
0
    if (vec == NULL) {
427
0
        vec = IGRAPH_CALLOC(1, igraph_vector_t);
428
0
        IGRAPH_CHECK_OOM(vec, "Cannot permute attributes.");
429
0
        IGRAPH_FINALLY(igraph_free, vec);
430
0
        IGRAPH_CHECK(igraph_vector_init(vec, work_area->length));
431
0
        work_area->numeric = vec;
432
0
        IGRAPH_FINALLY_CLEAN(1);
433
0
    }
434
435
0
    return IGRAPH_SUCCESS;
436
0
}
437
438
static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_boolean(
439
  igraph_i_attribute_permutation_work_area_t *work_area
440
0
) {
441
0
    igraph_vector_bool_t* vec = work_area->boolean;
442
443
0
    if (vec == NULL) {
444
0
        vec = IGRAPH_CALLOC(1, igraph_vector_bool_t);
445
0
        IGRAPH_CHECK_OOM(vec, "Cannot permute attributes.");
446
0
        IGRAPH_FINALLY(igraph_free, vec);
447
0
        IGRAPH_CHECK(igraph_vector_bool_init(vec, work_area->length));
448
0
        work_area->boolean = vec;
449
0
        IGRAPH_FINALLY_CLEAN(1);
450
0
    }
451
452
0
    return IGRAPH_SUCCESS;
453
0
}
454
455
static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_strings(
456
  igraph_i_attribute_permutation_work_area_t *work_area
457
0
) {
458
0
    igraph_vector_ptr_t* vec = work_area->strings;
459
460
0
    if (vec == NULL) {
461
0
        vec = IGRAPH_CALLOC(1, igraph_vector_ptr_t);
462
0
        IGRAPH_CHECK_OOM(vec, "Cannot permute attributes.");
463
0
        IGRAPH_FINALLY(igraph_free, vec);
464
0
        IGRAPH_CHECK(igraph_vector_ptr_init(vec, 0));
465
0
        IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(vec, igraph_strvector_destroy);
466
0
        work_area->strings = vec;
467
0
        IGRAPH_FINALLY_CLEAN(1);
468
0
    }
469
470
0
    return IGRAPH_SUCCESS;
471
0
}
472
473
static igraph_error_t igraph_i_attribute_permutation_work_area_permute_and_store_strvector(
474
  igraph_i_attribute_permutation_work_area_t *work_area,
475
  const igraph_strvector_t *vec,
476
  const igraph_vector_int_t *idx
477
0
) {
478
0
    igraph_strvector_t *new_vec;
479
480
0
    new_vec = IGRAPH_CALLOC(1, igraph_strvector_t);
481
0
    IGRAPH_CHECK_OOM(new_vec, "Cannot permute attributes.");
482
0
    IGRAPH_FINALLY(igraph_free, new_vec);
483
0
    IGRAPH_CHECK(igraph_strvector_init(new_vec, 0));
484
0
    IGRAPH_FINALLY(igraph_strvector_destroy, new_vec);
485
0
    IGRAPH_CHECK(igraph_vector_ptr_push_back(work_area->strings, new_vec));
486
0
    IGRAPH_FINALLY_CLEAN(2);
487
488
0
    IGRAPH_CHECK(igraph_strvector_index(vec, new_vec, idx));
489
490
0
    return IGRAPH_SUCCESS;
491
0
}
492
493
static igraph_error_t igraph_i_cattribute_permute_attribute_record_list(
494
    igraph_attribute_record_list_t *attrs,
495
    igraph_attribute_record_list_t *new_attrs,
496
    const igraph_vector_int_t *idx
497
0
) {
498
0
    igraph_int_t no_attrs, idxlen;
499
500
0
    no_attrs = igraph_attribute_record_list_size(attrs);
501
502
    /* When vertices or edges  are permuted, we now assume that there are no
503
     * attributes in the target attribute list yet */
504
0
    IGRAPH_ASSERT(igraph_attribute_record_list_empty(new_attrs));
505
0
    IGRAPH_FINALLY(igraph_attribute_record_list_clear, new_attrs);
506
507
0
    idxlen = igraph_vector_int_size(idx);
508
0
    for (igraph_int_t i = 0; i < no_attrs; i++) {
509
0
        igraph_attribute_record_t *oldrec = igraph_attribute_record_list_get_ptr(attrs, i);
510
0
        igraph_attribute_type_t type = oldrec->type;
511
512
        /* Create a record for the same attribute in the new graph */
513
0
        igraph_attribute_record_t *newrec;
514
0
        IGRAPH_CHECK(igraph_i_cattribute_find_or_create(
515
0
            new_attrs, oldrec->name, oldrec->type, idxlen, &newrec
516
0
        ));
517
518
        /* The data */
519
0
        switch (type) {
520
0
        case IGRAPH_ATTRIBUTE_NUMERIC:
521
0
            IGRAPH_CHECK(igraph_vector_index(
522
0
                oldrec->value.as_vector, newrec->value.as_vector, idx
523
0
            ));
524
0
            break;
525
0
        case IGRAPH_ATTRIBUTE_BOOLEAN:
526
0
            IGRAPH_CHECK(igraph_vector_bool_index(
527
0
                oldrec->value.as_vector_bool, newrec->value.as_vector_bool, idx
528
0
            ));
529
0
            break;
530
0
        case IGRAPH_ATTRIBUTE_STRING:
531
0
            IGRAPH_CHECK(igraph_strvector_index(
532
0
                oldrec->value.as_strvector, newrec->value.as_strvector, idx
533
0
            ));
534
0
            break;
535
0
        default:
536
0
            IGRAPH_WARNINGF(
537
0
                "Attribute '%s' with unknown type %d ignored",
538
0
                oldrec->name, (int) oldrec->type
539
0
            );
540
0
        }
541
0
    }
542
543
0
    IGRAPH_FINALLY_CLEAN(1);
544
545
0
    return IGRAPH_SUCCESS;
546
0
}
547
548
static igraph_error_t igraph_i_cattribute_permute_attribute_record_list_in_place(
549
    igraph_attribute_record_list_t *attrs, const igraph_vector_int_t *idx
550
0
) {
551
0
    igraph_int_t no_attrs = igraph_attribute_record_list_size(attrs);
552
0
    igraph_attribute_record_t *oldrec;
553
0
    igraph_vector_t *num, *num_work;
554
0
    igraph_strvector_t *str, str_work;
555
0
    igraph_vector_bool_t *oldbool, *bool_work;
556
0
    igraph_i_attribute_permutation_work_area_t work_area;
557
0
    igraph_int_t idx_size = igraph_vector_int_size(idx);
558
559
    /* shortcut: don't allocate anything if there are no attributes */
560
0
    if (no_attrs == 0) {
561
0
        return IGRAPH_SUCCESS;
562
0
    }
563
564
    /* do all the allocations that can potentially fail before we actually
565
     * start to permute the vertices to ensure that we will not ever need to
566
     * back out from a permutation once we've started it */
567
0
    IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_init(&work_area, idx_size));
568
0
    IGRAPH_FINALLY(igraph_i_attribute_permutation_work_area_destroy, &work_area);
569
0
    for (igraph_int_t i = 0; i < no_attrs; i++) {
570
0
        oldrec = igraph_attribute_record_list_get_ptr(attrs, i);
571
0
        switch (oldrec->type) {
572
0
        case IGRAPH_ATTRIBUTE_NUMERIC:
573
0
            num = oldrec->value.as_vector;
574
0
            IGRAPH_CHECK(igraph_vector_reserve(num, idx_size));
575
0
            IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_numeric(&work_area));
576
0
            break;
577
578
0
        case IGRAPH_ATTRIBUTE_BOOLEAN:
579
0
            oldbool = oldrec->value.as_vector_bool;
580
0
            IGRAPH_CHECK(igraph_vector_bool_reserve(oldbool, idx_size));
581
0
            IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_boolean(&work_area));
582
0
            break;
583
584
0
        case IGRAPH_ATTRIBUTE_STRING:
585
0
            str = oldrec->value.as_strvector;
586
0
            IGRAPH_CHECK(igraph_strvector_reserve(str, idx_size));
587
0
            IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_strings(&work_area));
588
0
            break;
589
590
0
        default:
591
0
            IGRAPH_WARNINGF(
592
0
                "Vertex attribute '%s' with unknown type %d ignored",
593
0
                oldrec->name, (int) oldrec->type
594
0
            );
595
0
        }
596
0
    }
597
598
    /* let's do string attributes first because these might need extra
599
     * allocations that can fail. The strategy is to build new igraph_strvector_t
600
     * instances for the permuted attributes and store them in an
601
     * igraph_vector_ptr_t until we are done with all of them. If any of the
602
     * allocations fail, we can destroy the igraph_vector_ptr_t safely */
603
0
    for (igraph_int_t i = 0; i < no_attrs; i++) {
604
0
        oldrec = igraph_attribute_record_list_get_ptr(attrs, i);
605
0
        if (oldrec->type == IGRAPH_ATTRIBUTE_STRING) {
606
0
            str = oldrec->value.as_strvector;
607
0
            IGRAPH_CHECK(
608
0
                igraph_i_attribute_permutation_work_area_permute_and_store_strvector(
609
0
                    &work_area, str, idx
610
0
                )
611
0
            );
612
0
        }
613
0
    }
614
615
    /* strings are done, and now all vectors involved in the process are
616
     * as large as they should be (or larger) so the operations below are not
617
     * supposed to fail. We can safely replace the original string attribute
618
     * vectors with the permuted ones, and then proceed to the remaining
619
     * attributes */
620
0
    for (igraph_int_t i = 0, j = 0; i < no_attrs; i++) {
621
0
        oldrec = igraph_attribute_record_list_get_ptr(attrs, i);
622
0
        if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) {
623
0
            continue;
624
0
        }
625
626
0
        str = oldrec->value.as_strvector;
627
0
        str_work = *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]);
628
0
        *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]) = *str;
629
0
        *str = str_work;
630
0
        j++;
631
0
    }
632
0
    igraph_i_attribute_permutation_work_area_release_stored_strvectors(&work_area);
633
634
0
    for (igraph_int_t i = 0; i < no_attrs; i++) {
635
0
        oldrec = igraph_attribute_record_list_get_ptr(attrs, i);
636
0
        switch (oldrec->type) {
637
0
        case IGRAPH_ATTRIBUTE_NUMERIC:
638
0
            num = oldrec->value.as_vector;
639
0
            num_work = work_area.numeric;
640
0
            IGRAPH_ASSERT(num_work != NULL);
641
0
            IGRAPH_CHECK(igraph_vector_index(num, num_work, idx));
642
0
            igraph_vector_swap(num, num_work);
643
0
            break;
644
0
        case IGRAPH_ATTRIBUTE_BOOLEAN:
645
0
            oldbool = oldrec->value.as_vector_bool;
646
0
            bool_work = work_area.boolean;
647
0
            IGRAPH_ASSERT(bool_work != NULL);
648
0
            IGRAPH_CHECK(igraph_vector_bool_index(oldbool, bool_work, idx));
649
0
            igraph_vector_bool_swap(oldbool, bool_work);
650
0
            break;
651
0
        case IGRAPH_ATTRIBUTE_STRING:
652
            /* nothing to do */
653
0
            break;
654
0
        default:
655
            /* already warned */
656
0
            break;
657
0
        }
658
0
    }
659
660
0
    igraph_i_attribute_permutation_work_area_destroy(&work_area);
661
0
    IGRAPH_FINALLY_CLEAN(1);
662
663
0
    return IGRAPH_SUCCESS;
664
0
}
665
666
static igraph_error_t igraph_i_cattribute_permute_vertices(
667
    const igraph_t *graph, igraph_t *newgraph, const igraph_vector_int_t *idx
668
0
) {
669
0
    igraph_i_cattributes_t *attr = graph->attr, *new_attr = newgraph->attr;
670
0
    igraph_attribute_record_list_t *val = &attr->val, *new_val = &new_attr->val;
671
0
    if (graph == newgraph) {
672
0
        return igraph_i_cattribute_permute_attribute_record_list_in_place(val, idx);
673
0
    } else {
674
0
        return igraph_i_cattribute_permute_attribute_record_list(val, new_val, idx);
675
0
    }
676
0
}
677
678
typedef igraph_error_t igraph_cattributes_combine_num_t(const igraph_vector_t *input,
679
        igraph_real_t *output);
680
681
typedef igraph_error_t igraph_cattributes_combine_str_t(const igraph_strvector_t *input,
682
        char **output);
683
684
typedef igraph_error_t igraph_cattributes_combine_bool_t(const igraph_vector_bool_t *input,
685
        igraph_bool_t *output);
686
687
static igraph_error_t igraph_i_cattributes_cn_sum(const igraph_attribute_record_t *oldrec,
688
                                       igraph_attribute_record_t * newrec,
689
0
                                       const igraph_vector_int_list_t *merges) {
690
0
    const igraph_vector_t *oldv = oldrec->value.as_vector;
691
0
    igraph_vector_t *newv = newrec->value.as_vector;
692
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
693
694
0
    for (igraph_int_t i = 0; i < newlen; i++) {
695
0
        igraph_real_t s = 0.0;
696
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
697
0
        igraph_int_t n = igraph_vector_int_size(idx);
698
0
        for (igraph_int_t j = 0; j < n; j++) {
699
0
            igraph_int_t x = VECTOR(*idx)[j];
700
0
            s += VECTOR(*oldv)[x];
701
0
        }
702
0
        VECTOR(*newv)[i] = s;
703
0
    }
704
705
0
    return IGRAPH_SUCCESS;
706
0
}
707
708
static igraph_error_t igraph_i_cattributes_cn_prod(const igraph_attribute_record_t *oldrec,
709
                                        igraph_attribute_record_t * newrec,
710
0
                                        const igraph_vector_int_list_t *merges) {
711
0
    const igraph_vector_t *oldv = oldrec->value.as_vector;
712
0
    igraph_vector_t *newv = newrec->value.as_vector;
713
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
714
715
0
    for (igraph_int_t i = 0; i < newlen; i++) {
716
0
        igraph_real_t s = 1.0;
717
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
718
0
        igraph_int_t n = igraph_vector_int_size(idx);
719
0
        for (igraph_int_t j = 0; j < n; j++) {
720
0
            igraph_int_t x = VECTOR(*idx)[j];
721
0
            s *= VECTOR(*oldv)[x];
722
0
        }
723
0
        VECTOR(*newv)[i] = s;
724
0
    }
725
726
0
    return IGRAPH_SUCCESS;
727
0
}
728
729
static igraph_error_t igraph_i_cattributes_cn_min(const igraph_attribute_record_t *oldrec,
730
                                       igraph_attribute_record_t * newrec,
731
0
                                       const igraph_vector_int_list_t *merges) {
732
0
    const igraph_vector_t *oldv = oldrec->value.as_vector;
733
0
    igraph_vector_t *newv = newrec->value.as_vector;
734
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
735
736
0
    for (igraph_int_t i = 0; i < newlen; i++) {
737
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
738
0
        igraph_int_t n = igraph_vector_int_size(idx);
739
0
        igraph_real_t m = n > 0 ? VECTOR(*oldv)[ VECTOR(*idx)[0] ] : IGRAPH_NAN;
740
0
        for (igraph_int_t j = 1; j < n; j++) {
741
0
            igraph_int_t x = VECTOR(*idx)[j];
742
0
            igraph_real_t val = VECTOR(*oldv)[x];
743
0
            if (val < m) {
744
0
                m = val;
745
0
            }
746
0
        }
747
0
        VECTOR(*newv)[i] = m;
748
0
    }
749
750
0
    return IGRAPH_SUCCESS;
751
0
}
752
753
static igraph_error_t igraph_i_cattributes_cn_max(const igraph_attribute_record_t *oldrec,
754
                                       igraph_attribute_record_t * newrec,
755
0
                                       const igraph_vector_int_list_t *merges) {
756
0
    const igraph_vector_t *oldv = oldrec->value.as_vector;
757
0
    igraph_vector_t *newv = newrec->value.as_vector;
758
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
759
760
0
    for (igraph_int_t i = 0; i < newlen; i++) {
761
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
762
0
        igraph_int_t n = igraph_vector_int_size(idx);
763
0
        igraph_real_t m = n > 0 ? VECTOR(*oldv)[ VECTOR(*idx)[0] ] : IGRAPH_NAN;
764
0
        for (igraph_int_t j = 1; j < n; j++) {
765
0
            igraph_int_t x = VECTOR(*idx)[j];
766
0
            igraph_real_t val = VECTOR(*oldv)[x];
767
0
            if (val > m) {
768
0
                m = val;
769
0
            }
770
0
        }
771
0
        VECTOR(*newv)[i] = m;
772
0
    }
773
774
0
    return IGRAPH_SUCCESS;
775
0
}
776
777
static igraph_error_t igraph_i_cattributes_cn_random(const igraph_attribute_record_t *oldrec,
778
                                          igraph_attribute_record_t * newrec,
779
0
                                          const igraph_vector_int_list_t *merges) {
780
781
0
    const igraph_vector_t *oldv = oldrec->value.as_vector;
782
0
    igraph_vector_t *newv = newrec->value.as_vector;
783
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
784
785
0
    for (igraph_int_t i = 0; i < newlen; i++) {
786
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
787
0
        igraph_int_t n = igraph_vector_int_size(idx);
788
0
        if (n == 0) {
789
0
            VECTOR(*newv)[i] = IGRAPH_NAN;
790
0
        } else if (n == 1) {
791
0
            VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ];
792
0
        } else {
793
0
            igraph_int_t r = RNG_INTEGER(0, n - 1);
794
0
            VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[r] ];
795
0
        }
796
0
    }
797
798
0
    return IGRAPH_SUCCESS;
799
0
}
800
801
static igraph_error_t igraph_i_cattributes_cn_first(const igraph_attribute_record_t *oldrec,
802
                                         igraph_attribute_record_t * newrec,
803
0
                                         const igraph_vector_int_list_t *merges) {
804
805
0
    const igraph_vector_t *oldv = oldrec->value.as_vector;
806
0
    igraph_vector_t *newv = newrec->value.as_vector;
807
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
808
809
0
    for (igraph_int_t i = 0; i < newlen; i++) {
810
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
811
0
        igraph_int_t n = igraph_vector_int_size(idx);
812
0
        if (n == 0) {
813
0
            VECTOR(*newv)[i] = IGRAPH_NAN;
814
0
        } else {
815
0
            VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ];
816
0
        }
817
0
    }
818
819
0
    return IGRAPH_SUCCESS;
820
0
}
821
822
static igraph_error_t igraph_i_cattributes_cn_last(const igraph_attribute_record_t *oldrec,
823
                                        igraph_attribute_record_t * newrec,
824
0
                                        const igraph_vector_int_list_t *merges) {
825
826
0
    const igraph_vector_t *oldv = oldrec->value.as_vector;
827
0
    igraph_vector_t *newv = newrec->value.as_vector;
828
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
829
830
0
    for (igraph_int_t i = 0; i < newlen; i++) {
831
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
832
0
        igraph_int_t n = igraph_vector_int_size(idx);
833
0
        if (n == 0) {
834
0
            VECTOR(*newv)[i] = IGRAPH_NAN;
835
0
        } else {
836
0
            VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[n - 1] ];
837
0
        }
838
0
    }
839
840
0
    return IGRAPH_SUCCESS;
841
0
}
842
843
static igraph_error_t igraph_i_cattributes_cn_mean(const igraph_attribute_record_t *oldrec,
844
                                        igraph_attribute_record_t * newrec,
845
0
                                        const igraph_vector_int_list_t *merges) {
846
0
    const igraph_vector_t *oldv = oldrec->value.as_vector;
847
0
    igraph_vector_t *newv = newrec->value.as_vector;
848
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
849
850
0
    for (igraph_int_t i = 0; i < newlen; i++) {
851
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
852
0
        igraph_int_t n = igraph_vector_int_size(idx);
853
0
        igraph_real_t s = n > 0 ? 0.0 : IGRAPH_NAN;
854
0
        for (igraph_int_t j = 0; j < n; j++) {
855
0
            igraph_int_t x = VECTOR(*idx)[j];
856
0
            s += VECTOR(*oldv)[x];
857
0
        }
858
0
        if (n > 0) {
859
0
            s = s / n;
860
0
        }
861
0
        VECTOR(*newv)[i] = s;
862
0
    }
863
864
0
    return IGRAPH_SUCCESS;
865
0
}
866
867
static igraph_error_t igraph_i_cattributes_cn_func(const igraph_attribute_record_t *oldrec,
868
                                        igraph_attribute_record_t *newrec,
869
                                        const igraph_vector_int_list_t *merges,
870
0
                                        igraph_cattributes_combine_num_t *func) {
871
872
0
    const igraph_vector_t *oldv = oldrec->value.as_vector;
873
0
    igraph_vector_t *newv = newrec->value.as_vector;
874
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
875
876
0
    igraph_vector_t values;
877
0
    IGRAPH_VECTOR_INIT_FINALLY(&values, 0);
878
879
0
    for (igraph_int_t i = 0; i < newlen; i++) {
880
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
881
882
0
        igraph_int_t n = igraph_vector_int_size(idx);
883
0
        IGRAPH_CHECK(igraph_vector_resize(&values, n));
884
0
        for (igraph_int_t j = 0; j < n; j++) {
885
0
            igraph_int_t x = VECTOR(*idx)[j];
886
0
            VECTOR(values)[j] = VECTOR(*oldv)[x];
887
0
        }
888
889
0
        igraph_real_t res;
890
0
        IGRAPH_CHECK(func(&values, &res));
891
0
        VECTOR(*newv)[i] = res;
892
0
    }
893
894
0
    igraph_vector_destroy(&values);
895
0
    IGRAPH_FINALLY_CLEAN(1);
896
897
0
    return IGRAPH_SUCCESS;
898
0
}
899
900
static igraph_error_t igraph_i_cattributes_cb_random(const igraph_attribute_record_t *oldrec,
901
                                          igraph_attribute_record_t * newrec,
902
0
                                          const igraph_vector_int_list_t *merges) {
903
904
0
    const igraph_vector_bool_t *oldv = oldrec->value.as_vector_bool;
905
0
    igraph_vector_bool_t *newv = newrec->value.as_vector_bool;
906
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
907
908
0
    for (igraph_int_t i = 0; i < newlen; i++) {
909
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
910
0
        igraph_int_t n = igraph_vector_int_size(idx);
911
0
        if (n == 0) {
912
0
            VECTOR(*newv)[i] = 0;
913
0
        } else if (n == 1) {
914
0
            VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ];
915
0
        } else {
916
0
            igraph_int_t r = RNG_INTEGER(0, n - 1);
917
0
            VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[r] ];
918
0
        }
919
0
    }
920
921
0
    return IGRAPH_SUCCESS;
922
0
}
923
924
static igraph_error_t igraph_i_cattributes_cb_first(const igraph_attribute_record_t *oldrec,
925
                                         igraph_attribute_record_t * newrec,
926
0
                                         const igraph_vector_int_list_t *merges) {
927
928
0
    const igraph_vector_bool_t *oldv = oldrec->value.as_vector_bool;
929
0
    igraph_vector_bool_t *newv = newrec->value.as_vector_bool;
930
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
931
932
0
    for (igraph_int_t i = 0; i < newlen; i++) {
933
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
934
0
        igraph_int_t n = igraph_vector_int_size(idx);
935
0
        if (n == 0) {
936
0
            VECTOR(*newv)[i] = 0;
937
0
        } else {
938
0
            VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ];
939
0
        }
940
0
    }
941
942
0
    return IGRAPH_SUCCESS;
943
0
}
944
945
static igraph_error_t igraph_i_cattributes_cb_last(const igraph_attribute_record_t *oldrec,
946
                                        igraph_attribute_record_t * newrec,
947
0
                                        const igraph_vector_int_list_t *merges) {
948
949
0
    const igraph_vector_bool_t *oldv = oldrec->value.as_vector_bool;
950
0
    igraph_vector_bool_t *newv = newrec->value.as_vector_bool;
951
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
952
953
0
    for (igraph_int_t i = 0; i < newlen; i++) {
954
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
955
0
        igraph_int_t n = igraph_vector_int_size(idx);
956
0
        if (n == 0) {
957
0
            VECTOR(*newv)[i] = 0;
958
0
        } else {
959
0
            VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[n - 1] ];
960
0
        }
961
0
    }
962
963
0
    return IGRAPH_SUCCESS;
964
0
}
965
966
static igraph_error_t igraph_i_cattributes_cb_all_is_true(const igraph_attribute_record_t *oldrec,
967
                                               igraph_attribute_record_t * newrec,
968
0
                                               const igraph_vector_int_list_t *merges) {
969
970
0
    const igraph_vector_bool_t *oldv = oldrec->value.as_vector_bool;
971
0
    igraph_vector_bool_t *newv = newrec->value.as_vector_bool;
972
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
973
974
0
    for (igraph_int_t i = 0; i < newlen; i++) {
975
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
976
0
        igraph_int_t n = igraph_vector_int_size(idx);
977
0
        VECTOR(*newv)[i] = 1;
978
0
        for (igraph_int_t j = 0; j < n; j++) {
979
0
            igraph_int_t x = VECTOR(*idx)[j];
980
0
            if (!VECTOR(*oldv)[x]) {
981
0
                VECTOR(*newv)[i] = 0;
982
0
                break;
983
0
            }
984
0
        }
985
0
    }
986
987
0
    return IGRAPH_SUCCESS;
988
0
}
989
990
static igraph_error_t igraph_i_cattributes_cb_any_is_true(const igraph_attribute_record_t *oldrec,
991
                                               igraph_attribute_record_t * newrec,
992
0
                                               const igraph_vector_int_list_t *merges) {
993
994
0
    const igraph_vector_bool_t *oldv = oldrec->value.as_vector_bool;
995
0
    igraph_vector_bool_t *newv = newrec->value.as_vector_bool;
996
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
997
998
0
    for (igraph_int_t i = 0; i < newlen; i++) {
999
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
1000
0
        igraph_int_t n = igraph_vector_int_size(idx);
1001
0
        VECTOR(*newv)[i] = 0;
1002
0
        for (igraph_int_t j = 0; j < n; j++) {
1003
0
            igraph_int_t x = VECTOR(*idx)[j];
1004
0
            if (VECTOR(*oldv)[x]) {
1005
0
                VECTOR(*newv)[i] = 1;
1006
0
                break;
1007
0
            }
1008
0
        }
1009
0
    }
1010
1011
0
    return IGRAPH_SUCCESS;
1012
0
}
1013
1014
static igraph_error_t igraph_i_cattributes_cb_majority(const igraph_attribute_record_t *oldrec,
1015
                                            igraph_attribute_record_t * newrec,
1016
0
                                            const igraph_vector_int_list_t *merges) {
1017
1018
0
    const igraph_vector_bool_t *oldv = oldrec->value.as_vector_bool;
1019
0
    igraph_vector_bool_t *newv = newrec->value.as_vector_bool;
1020
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
1021
1022
0
    for (igraph_int_t i = 0; i < newlen; i++) {
1023
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
1024
0
        igraph_int_t n = igraph_vector_int_size(idx);
1025
1026
0
        igraph_int_t num_trues = 0;
1027
0
        for (igraph_int_t j = 0; j < n; j++) {
1028
0
            igraph_int_t x = VECTOR(*idx)[j];
1029
0
            if (VECTOR(*oldv)[x]) {
1030
0
                num_trues++;
1031
0
            }
1032
0
        }
1033
1034
0
        if (n % 2 != 0) {
1035
0
            VECTOR(*newv)[i] = (num_trues > n / 2);
1036
0
        } else {
1037
0
            if (num_trues == n / 2) {
1038
0
                VECTOR(*newv)[i] = RNG_BOOL();
1039
0
            } else {
1040
0
                VECTOR(*newv)[i] = (num_trues > n / 2);
1041
0
            }
1042
0
        }
1043
0
    }
1044
1045
0
    return IGRAPH_SUCCESS;
1046
0
}
1047
1048
static igraph_error_t igraph_i_cattributes_cb_func(const igraph_attribute_record_t *oldrec,
1049
                                        igraph_attribute_record_t *newrec,
1050
                                        const igraph_vector_int_list_t *merges,
1051
0
                                        igraph_cattributes_combine_bool_t *func) {
1052
1053
0
    const igraph_vector_bool_t *oldv = oldrec->value.as_vector_bool;
1054
0
    igraph_vector_bool_t *newv = newrec->value.as_vector_bool;
1055
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
1056
1057
0
    igraph_vector_bool_t values;
1058
0
    IGRAPH_VECTOR_BOOL_INIT_FINALLY(&values, 0);
1059
1060
0
    for (igraph_int_t i = 0; i < newlen; i++) {
1061
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
1062
1063
0
        igraph_int_t n = igraph_vector_int_size(idx);
1064
0
        IGRAPH_CHECK(igraph_vector_bool_resize(&values, n));
1065
0
        for (igraph_int_t j = 0; j < n; j++) {
1066
0
            igraph_int_t x = VECTOR(*idx)[j];
1067
0
            VECTOR(values)[j] = VECTOR(*oldv)[x];
1068
0
        }
1069
1070
0
        igraph_bool_t res;
1071
0
        IGRAPH_CHECK(func(&values, &res));
1072
0
        VECTOR(*newv)[i] = res;
1073
0
    }
1074
1075
0
    igraph_vector_bool_destroy(&values);
1076
0
    IGRAPH_FINALLY_CLEAN(1);
1077
1078
0
    return IGRAPH_SUCCESS;
1079
0
}
1080
1081
static igraph_error_t igraph_i_cattributes_sn_random(const igraph_attribute_record_t *oldrec,
1082
                                          igraph_attribute_record_t *newrec,
1083
0
                                          const igraph_vector_int_list_t *merges) {
1084
1085
0
    const igraph_strvector_t *oldv = oldrec->value.as_strvector;
1086
0
    igraph_strvector_t *newv = newrec->value.as_strvector;
1087
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
1088
1089
0
    for (igraph_int_t i = 0; i < newlen; i++) {
1090
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
1091
0
        igraph_int_t n = igraph_vector_int_size(idx);
1092
0
        const char *tmp;
1093
0
        if (n == 0) {
1094
0
            IGRAPH_CHECK(igraph_strvector_set(newv, i, ""));
1095
0
        } else if (n == 1) {
1096
0
            tmp = igraph_strvector_get(oldv, 0);
1097
0
            IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp));
1098
0
        } else {
1099
0
            igraph_int_t r = RNG_INTEGER(0, n - 1);
1100
0
            tmp = igraph_strvector_get(oldv, r);
1101
0
            IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp));
1102
0
        }
1103
0
    }
1104
1105
0
    return IGRAPH_SUCCESS;
1106
0
}
1107
1108
static igraph_error_t igraph_i_cattributes_cs_first(const igraph_attribute_record_t *oldrec,
1109
                                         igraph_attribute_record_t *newrec,
1110
0
                                         const igraph_vector_int_list_t *merges) {
1111
1112
0
    const igraph_strvector_t *oldv = oldrec->value.as_strvector;
1113
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
1114
0
    igraph_strvector_t *newv = newrec->value.as_strvector;
1115
1116
0
    for (igraph_int_t i = 0; i < newlen; i++) {
1117
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
1118
0
        igraph_int_t n = igraph_vector_int_size(idx);
1119
0
        if (n == 0) {
1120
0
            IGRAPH_CHECK(igraph_strvector_set(newv, i, ""));
1121
0
        } else {
1122
0
            const char *tmp = igraph_strvector_get(oldv, VECTOR(*idx)[0]);
1123
0
            IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp));
1124
0
        }
1125
0
    }
1126
1127
0
    return IGRAPH_SUCCESS;
1128
0
}
1129
1130
static igraph_error_t igraph_i_cattributes_cs_last(const igraph_attribute_record_t *oldrec,
1131
                                        igraph_attribute_record_t *newrec,
1132
0
                                        const igraph_vector_int_list_t *merges) {
1133
1134
0
    const igraph_strvector_t *oldv = oldrec->value.as_strvector;
1135
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
1136
0
    igraph_strvector_t *newv = newrec->value.as_strvector;
1137
1138
0
    for (igraph_int_t i = 0; i < newlen; i++) {
1139
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
1140
0
        igraph_int_t n = igraph_vector_int_size(idx);
1141
0
        if (n == 0) {
1142
0
            IGRAPH_CHECK(igraph_strvector_set(newv, i, ""));
1143
0
        } else {
1144
0
            const char *tmp = igraph_strvector_get(oldv, VECTOR(*idx)[n - 1]);
1145
0
            IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp));
1146
0
        }
1147
0
    }
1148
1149
0
    return IGRAPH_SUCCESS;
1150
0
}
1151
1152
static igraph_error_t igraph_i_cattributes_cs_concat(const igraph_attribute_record_t *oldrec,
1153
                                          igraph_attribute_record_t *newrec,
1154
0
                                          const igraph_vector_int_list_t *merges) {
1155
1156
0
    const igraph_strvector_t *oldv = oldrec->value.as_strvector;
1157
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
1158
0
    igraph_strvector_t *newv = newrec->value.as_strvector;
1159
1160
0
    for (igraph_int_t i = 0; i < newlen; i++) {
1161
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
1162
0
        igraph_int_t n = igraph_vector_int_size(idx);
1163
0
        size_t len = 0;
1164
0
        const char *tmp;
1165
0
        char *tmp2;
1166
0
        for (igraph_int_t j = 0; j < n; j++) {
1167
0
            tmp = igraph_strvector_get(oldv, j);
1168
0
            len += strlen(tmp);
1169
0
        }
1170
0
        tmp2 = IGRAPH_CALLOC(len + 1, char);
1171
0
        IGRAPH_CHECK_OOM(tmp2, "Cannot combine attributes.");
1172
0
        IGRAPH_FINALLY(igraph_free, tmp2);
1173
0
        len = 0;
1174
0
        for (igraph_int_t j = 0; j < n; j++) {
1175
0
            tmp = igraph_strvector_get(oldv, j);
1176
0
            strcpy(tmp2 + len, tmp);
1177
0
            len += strlen(tmp);
1178
0
        }
1179
1180
0
        IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp2));
1181
0
        IGRAPH_FREE(tmp2);
1182
0
        IGRAPH_FINALLY_CLEAN(1);
1183
0
    }
1184
1185
0
    return IGRAPH_SUCCESS;
1186
0
}
1187
1188
static igraph_error_t igraph_i_cattributes_cs_func(const igraph_attribute_record_t *oldrec,
1189
                                        igraph_attribute_record_t *newrec,
1190
                                        const igraph_vector_int_list_t *merges,
1191
0
                                        igraph_cattributes_combine_str_t *func) {
1192
1193
0
    const igraph_strvector_t *oldv = oldrec->value.as_strvector;
1194
0
    igraph_strvector_t *newv = newrec->value.as_strvector;
1195
0
    igraph_int_t newlen = igraph_vector_int_list_size(merges);
1196
1197
0
    igraph_strvector_t values;
1198
0
    IGRAPH_STRVECTOR_INIT_FINALLY(&values, 0);
1199
1200
0
    for (igraph_int_t i = 0; i < newlen; i++) {
1201
0
        const igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);
1202
1203
0
        igraph_int_t n = igraph_vector_int_size(idx);
1204
0
        IGRAPH_CHECK(igraph_strvector_resize(&values, n));
1205
0
        for (igraph_int_t j = 0; j < n; j++) {
1206
0
            igraph_int_t x = VECTOR(*idx)[j];
1207
0
            const char *elem = igraph_strvector_get(oldv, x);
1208
0
            IGRAPH_CHECK(igraph_strvector_set(newv, j, elem));
1209
0
        }
1210
1211
0
        char *res;
1212
0
        IGRAPH_CHECK(func(&values, &res));
1213
0
        IGRAPH_FINALLY(igraph_free, res);
1214
1215
0
        IGRAPH_CHECK(igraph_strvector_set(newv, i, res));
1216
1217
0
        IGRAPH_FREE(res);
1218
0
        IGRAPH_FINALLY_CLEAN(1);
1219
0
    }
1220
1221
0
    igraph_strvector_destroy(&values);
1222
0
    IGRAPH_FINALLY_CLEAN(1);
1223
1224
0
    return IGRAPH_SUCCESS;
1225
0
}
1226
1227
/**
1228
 * \section c_attribute_combination_functions
1229
 *
1230
 * <para>
1231
 * The C attribute handler supports combining the attributes of multiple
1232
 * vertices of edges into a single attribute during a vertex or edge contraction
1233
 * operation via a user-defined function. This is achieved by setting the
1234
 * type of the attribute combination to \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION
1235
 * and passing in a pointer to the custom combination function when specifying
1236
 * attribute combinations in \ref igraph_attribute_combination() or
1237
 * \ref igraph_attribute_combination_add() . For the C attribute handler, the
1238
 * signature of the function depends on the type of the underlying attribute.
1239
 * For numeric attributes, use:
1240
 * \verbatim igraph_error_t function(const igraph_vector_t *input, igraph_real_t *output); \endverbatim
1241
 * where \p input will receive a vector containing the value of the attribute
1242
 * for all the vertices or edges being combined, and \p output must be filled
1243
 * by the function to the combined value. Similarly, for Boolean attributes, the
1244
 * function takes a boolean vector in \p input and must return the combined Boolean
1245
 * value in \p output:
1246
 * \verbatim igraph_error_t function(const igraph_vector_bool_t *input, igraph_bool_t *output); \endverbatim
1247
 * For string attributes, the signature is slightly different:
1248
 * \verbatim igraph_error_t function(const igraph_strvector_t *input, char **output); \endverbatim
1249
 * In case of strings, all strings in the input vector are \em owned by igraph
1250
 * and must not be modified or freed in the combination handler. The string
1251
 * returned to the caller in \p output remains owned by the caller; igraph will
1252
 * make a copy it and store the copy in the appropriate part of the data
1253
 * structure holding the vertex or edge attributes.
1254
 * </para>
1255
 */
1256
1257
typedef struct {
1258
    igraph_attribute_combination_type_t type;
1259
    union {
1260
        igraph_function_pointer_t as_void;
1261
        igraph_cattributes_combine_num_t *as_num;
1262
        igraph_cattributes_combine_str_t *as_str;
1263
        igraph_cattributes_combine_bool_t *as_bool;
1264
    } func;
1265
} igraph_attribute_combination_todo_item_t;
1266
1267
static igraph_error_t igraph_i_cattribute_combine_attribute_record_lists(
1268
    igraph_attribute_record_list_t *attrs, igraph_attribute_record_list_t *new_attrs,
1269
    const igraph_vector_int_list_t *merges, const igraph_attribute_combination_t *comb
1270
0
) {
1271
0
    igraph_int_t no_attrs = igraph_attribute_record_list_size(attrs);
1272
0
    igraph_attribute_combination_todo_item_t *todo_items;
1273
1274
0
    IGRAPH_ASSERT(attrs != new_attrs);
1275
0
    IGRAPH_ASSERT(igraph_attribute_record_list_empty(new_attrs));
1276
1277
0
    todo_items = IGRAPH_CALLOC(no_attrs, igraph_attribute_combination_todo_item_t);
1278
0
    IGRAPH_CHECK_OOM(todo_items, "Cannot combine attributes.");
1279
0
    IGRAPH_FINALLY(igraph_free, todo_items);
1280
1281
0
    for (igraph_int_t i = 0; i < no_attrs; i++) {
1282
0
        const igraph_attribute_record_t *oldrec = igraph_attribute_record_list_get_ptr(attrs, i);
1283
0
        const char *name = oldrec->name;
1284
0
        igraph_attribute_combination_type_t type;
1285
0
        igraph_function_pointer_t voidfunc;
1286
0
        IGRAPH_CHECK(igraph_attribute_combination_query(comb, name, &type, &voidfunc));
1287
0
        todo_items[i].type = type;
1288
0
        todo_items[i].func.as_void = voidfunc;
1289
0
    }
1290
1291
0
    IGRAPH_FINALLY(igraph_attribute_record_list_clear, new_attrs);
1292
1293
0
    for (igraph_int_t i = 0; i < no_attrs; i++) {
1294
0
        const igraph_attribute_record_t *oldrec = igraph_attribute_record_list_get_ptr(attrs, i);
1295
0
        igraph_attribute_record_t newrec;
1296
0
        const char *name = oldrec->name;
1297
0
        igraph_attribute_combination_todo_item_t todo_item = todo_items[i];
1298
0
        igraph_attribute_type_t attr_type = oldrec->type;
1299
1300
0
        if (todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_DEFAULT ||
1301
0
            todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_IGNORE) {
1302
0
            continue;
1303
0
        }
1304
1305
0
        IGRAPH_CHECK(igraph_attribute_record_init(&newrec, name, attr_type));
1306
0
        IGRAPH_FINALLY(igraph_attribute_record_destroy, &newrec);
1307
1308
0
        IGRAPH_CHECK(igraph_attribute_record_resize(&newrec, igraph_vector_int_list_size(merges)));
1309
1310
0
        if (attr_type == IGRAPH_ATTRIBUTE_NUMERIC) {
1311
0
            switch (todo_item.type) {
1312
0
            case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION:
1313
0
                IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, &newrec, merges,
1314
0
                             todo_item.func.as_num));
1315
0
                break;
1316
0
            case IGRAPH_ATTRIBUTE_COMBINE_SUM:
1317
0
                IGRAPH_CHECK(igraph_i_cattributes_cn_sum(oldrec, &newrec, merges));
1318
0
                break;
1319
0
            case IGRAPH_ATTRIBUTE_COMBINE_PROD:
1320
0
                IGRAPH_CHECK(igraph_i_cattributes_cn_prod(oldrec, &newrec, merges));
1321
0
                break;
1322
0
            case IGRAPH_ATTRIBUTE_COMBINE_MIN:
1323
0
                IGRAPH_CHECK(igraph_i_cattributes_cn_min(oldrec, &newrec, merges));
1324
0
                break;
1325
0
            case IGRAPH_ATTRIBUTE_COMBINE_MAX:
1326
0
                IGRAPH_CHECK(igraph_i_cattributes_cn_max(oldrec, &newrec, merges));
1327
0
                break;
1328
0
            case IGRAPH_ATTRIBUTE_COMBINE_RANDOM:
1329
0
                IGRAPH_CHECK(igraph_i_cattributes_cn_random(oldrec, &newrec, merges));
1330
0
                break;
1331
0
            case IGRAPH_ATTRIBUTE_COMBINE_FIRST:
1332
0
                IGRAPH_CHECK(igraph_i_cattributes_cn_first(oldrec, &newrec, merges));
1333
0
                break;
1334
0
            case IGRAPH_ATTRIBUTE_COMBINE_LAST:
1335
0
                IGRAPH_CHECK(igraph_i_cattributes_cn_last(oldrec, &newrec, merges));
1336
0
                break;
1337
0
            case IGRAPH_ATTRIBUTE_COMBINE_MEAN:
1338
0
                IGRAPH_CHECK(igraph_i_cattributes_cn_mean(oldrec, &newrec, merges));
1339
0
                break;
1340
0
            case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN:
1341
0
                IGRAPH_ERROR("Median calculation not implemented.",
1342
0
                             IGRAPH_UNIMPLEMENTED);
1343
0
                break;
1344
0
            case IGRAPH_ATTRIBUTE_COMBINE_CONCAT:
1345
0
                IGRAPH_ERROR("Cannot concatenate numeric attributes.",
1346
0
                             IGRAPH_EATTRCOMBINE);
1347
0
                break;
1348
0
            default:
1349
0
                IGRAPH_ERROR("Unknown attribute combination.",
1350
0
                             IGRAPH_UNIMPLEMENTED);
1351
0
                break;
1352
0
            }
1353
0
        } else if (attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) {
1354
0
            switch (todo_item.type) {
1355
0
            case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION:
1356
0
                IGRAPH_CHECK(igraph_i_cattributes_cb_func(oldrec, &newrec, merges,
1357
0
                             todo_item.func.as_bool));
1358
0
                break;
1359
0
            case IGRAPH_ATTRIBUTE_COMBINE_SUM:
1360
0
            case IGRAPH_ATTRIBUTE_COMBINE_MAX:
1361
0
                IGRAPH_CHECK(igraph_i_cattributes_cb_any_is_true(oldrec, &newrec, merges));
1362
0
                break;
1363
0
            case IGRAPH_ATTRIBUTE_COMBINE_PROD:
1364
0
            case IGRAPH_ATTRIBUTE_COMBINE_MIN:
1365
0
                IGRAPH_CHECK(igraph_i_cattributes_cb_all_is_true(oldrec, &newrec, merges));
1366
0
                break;
1367
0
            case IGRAPH_ATTRIBUTE_COMBINE_MEAN:
1368
0
            case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN:
1369
0
                IGRAPH_CHECK(igraph_i_cattributes_cb_majority(oldrec, &newrec, merges));
1370
0
                break;
1371
0
            case IGRAPH_ATTRIBUTE_COMBINE_RANDOM:
1372
0
                IGRAPH_CHECK(igraph_i_cattributes_cb_random(oldrec, &newrec, merges));
1373
0
                break;
1374
0
            case IGRAPH_ATTRIBUTE_COMBINE_FIRST:
1375
0
                IGRAPH_CHECK(igraph_i_cattributes_cb_first(oldrec, &newrec, merges));
1376
0
                break;
1377
0
            case IGRAPH_ATTRIBUTE_COMBINE_LAST:
1378
0
                IGRAPH_CHECK(igraph_i_cattributes_cb_last(oldrec, &newrec, merges));
1379
0
                break;
1380
0
            case IGRAPH_ATTRIBUTE_COMBINE_CONCAT:
1381
0
                IGRAPH_ERROR("Cannot calculate concatenation of Booleans.",
1382
0
                             IGRAPH_EATTRCOMBINE);
1383
0
                break;
1384
0
            default:
1385
0
                IGRAPH_ERROR("Unknown attribute combination.",
1386
0
                             IGRAPH_UNIMPLEMENTED);
1387
0
                break;
1388
0
            }
1389
0
        } else if (attr_type == IGRAPH_ATTRIBUTE_STRING) {
1390
0
            switch (todo_item.type) {
1391
0
            case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION:
1392
0
                IGRAPH_CHECK(igraph_i_cattributes_cs_func(oldrec, &newrec, merges,
1393
0
                             todo_item.func.as_str));
1394
0
                break;
1395
0
            case IGRAPH_ATTRIBUTE_COMBINE_SUM:
1396
0
                IGRAPH_ERROR("Cannot sum strings.", IGRAPH_EATTRCOMBINE);
1397
0
                break;
1398
0
            case IGRAPH_ATTRIBUTE_COMBINE_PROD:
1399
0
                IGRAPH_ERROR("Cannot multiply strings.", IGRAPH_EATTRCOMBINE);
1400
0
                break;
1401
0
            case IGRAPH_ATTRIBUTE_COMBINE_MIN:
1402
0
                IGRAPH_ERROR("Cannot find minimum of strings.",
1403
0
                             IGRAPH_EATTRCOMBINE);
1404
0
                break;
1405
0
            case IGRAPH_ATTRIBUTE_COMBINE_MAX:
1406
0
                IGRAPH_ERROR("Cannot find maximum of strings.",
1407
0
                             IGRAPH_EATTRCOMBINE);
1408
0
                break;
1409
0
            case IGRAPH_ATTRIBUTE_COMBINE_MEAN:
1410
0
                IGRAPH_ERROR("Cannot calculate mean of strings.",
1411
0
                             IGRAPH_EATTRCOMBINE);
1412
0
                break;
1413
0
            case IGRAPH_ATTRIBUTE_COMBINE_MEDIAN:
1414
0
                IGRAPH_ERROR("Cannot calculate median of strings.",
1415
0
                             IGRAPH_EATTRCOMBINE);
1416
0
                break;
1417
0
            case IGRAPH_ATTRIBUTE_COMBINE_RANDOM:
1418
0
                IGRAPH_CHECK(igraph_i_cattributes_sn_random(oldrec, &newrec, merges));
1419
0
                break;
1420
0
            case IGRAPH_ATTRIBUTE_COMBINE_FIRST:
1421
0
                IGRAPH_CHECK(igraph_i_cattributes_cs_first(oldrec, &newrec, merges));
1422
0
                break;
1423
0
            case IGRAPH_ATTRIBUTE_COMBINE_LAST:
1424
0
                IGRAPH_CHECK(igraph_i_cattributes_cs_last(oldrec, &newrec, merges));
1425
0
                break;
1426
0
            case IGRAPH_ATTRIBUTE_COMBINE_CONCAT:
1427
0
                IGRAPH_CHECK(igraph_i_cattributes_cs_concat(oldrec, &newrec, merges));
1428
0
                break;
1429
0
            default:
1430
0
                IGRAPH_ERROR("Unknown attribute combination.",
1431
0
                             IGRAPH_UNIMPLEMENTED);
1432
0
                break;
1433
0
            }
1434
0
        } else {
1435
0
            IGRAPH_ERROR("Unknown attribute type, this should not happen.",
1436
0
                         IGRAPH_UNIMPLEMENTED);
1437
0
        }
1438
1439
0
        IGRAPH_CHECK(igraph_attribute_record_list_push_back(new_attrs, &newrec));
1440
0
        IGRAPH_FINALLY_CLEAN(1);  /* ownership of newrec transferred */
1441
0
    }
1442
1443
0
    IGRAPH_FREE(todo_items);
1444
0
    IGRAPH_FINALLY_CLEAN(2);
1445
1446
0
    return IGRAPH_SUCCESS;
1447
0
}
1448
1449
static igraph_error_t igraph_i_cattribute_combine_vertices(
1450
    const igraph_t *graph, igraph_t *newgraph,
1451
    const igraph_vector_int_list_t *merges, const igraph_attribute_combination_t *comb
1452
0
) {
1453
0
    igraph_i_cattributes_t *attr = graph->attr;
1454
0
    igraph_i_cattributes_t *toattr = newgraph->attr;
1455
0
    return igraph_i_cattribute_combine_attribute_record_lists(
1456
0
        &attr->val, &toattr->val, merges, comb
1457
0
    );
1458
0
}
1459
1460
static igraph_error_t igraph_i_cattribute_add_edges(
1461
    igraph_t *graph, const igraph_vector_int_t *edges,
1462
    const igraph_attribute_record_list_t *nattr
1463
2.21k
) {
1464
2.21k
    igraph_int_t ne = igraph_vector_int_size(edges) / 2;
1465
2.21k
    igraph_i_cattributes_t *attr = graph->attr;
1466
2.21k
    return igraph_i_cattribute_add_vertices_or_edges(&attr->eal, igraph_ecount(graph), ne, nattr);
1467
2.21k
}
1468
1469
static igraph_error_t igraph_i_cattribute_permute_edges(const igraph_t *graph,
1470
                                             igraph_t *newgraph,
1471
0
                                             const igraph_vector_int_t *idx) {
1472
0
    igraph_i_cattributes_t *attr = graph->attr, *new_attr = newgraph->attr;
1473
0
    igraph_attribute_record_list_t *eal = &attr->eal, *new_eal = &new_attr->eal;
1474
0
    if (graph == newgraph) {
1475
0
        return igraph_i_cattribute_permute_attribute_record_list_in_place(eal, idx);
1476
0
    } else {
1477
0
        return igraph_i_cattribute_permute_attribute_record_list(eal, new_eal, idx);
1478
0
    }
1479
0
}
1480
1481
static igraph_error_t igraph_i_cattribute_combine_edges(
1482
    const igraph_t *graph, igraph_t *newgraph,
1483
    const igraph_vector_int_list_t *merges, const igraph_attribute_combination_t *comb
1484
0
) {
1485
0
    igraph_i_cattributes_t *attr = graph->attr;
1486
0
    igraph_i_cattributes_t *toattr = newgraph->attr;
1487
0
    return igraph_i_cattribute_combine_attribute_record_lists(
1488
0
        &attr->eal, &toattr->eal, merges, comb
1489
0
    );
1490
0
}
1491
1492
static igraph_error_t igraph_i_cattribute_get_info(const igraph_t *graph,
1493
                                        igraph_strvector_t *gnames,
1494
                                        igraph_vector_int_t *gtypes,
1495
                                        igraph_strvector_t *vnames,
1496
                                        igraph_vector_int_t *vtypes,
1497
                                        igraph_strvector_t *enames,
1498
0
                                        igraph_vector_int_t *etypes) {
1499
1500
0
    igraph_strvector_t *names[3] = { gnames, vnames, enames };
1501
0
    igraph_vector_int_t *types[3] = { gtypes, vtypes, etypes };
1502
0
    igraph_i_cattributes_t *at = graph->attr;
1503
0
    igraph_attribute_record_list_t *attr[3] = { &at->gal, &at->val, &at->eal };
1504
1505
0
    for (igraph_int_t i = 0; i < 3; i++) {
1506
0
        igraph_strvector_t *n = names[i];
1507
0
        igraph_vector_int_t *t = types[i];
1508
0
        const igraph_attribute_record_list_t *al = attr[i];
1509
0
        igraph_int_t len = igraph_attribute_record_list_size(al);
1510
1511
0
        if (n) {
1512
0
            IGRAPH_CHECK(igraph_strvector_resize(n, len));
1513
0
        }
1514
0
        if (t) {
1515
0
            IGRAPH_CHECK(igraph_vector_int_resize(t, len));
1516
0
        }
1517
1518
0
        for (igraph_int_t j = 0; j < len; j++) {
1519
0
            const igraph_attribute_record_t *rec = igraph_attribute_record_list_get_ptr(al, j);
1520
0
            const char *name = rec->name;
1521
0
            igraph_attribute_type_t type = rec->type;
1522
0
            if (n) {
1523
0
                IGRAPH_CHECK(igraph_strvector_set(n, j, name));
1524
0
            }
1525
0
            if (t) {
1526
0
                VECTOR(*t)[j] = type;
1527
0
            }
1528
0
        }
1529
0
    }
1530
1531
0
    return IGRAPH_SUCCESS;
1532
0
}
1533
1534
static igraph_bool_t igraph_i_cattribute_has_attr(const igraph_t *graph,
1535
                                                  igraph_attribute_elemtype_t type,
1536
121k
                                                  const char *name) {
1537
121k
    const igraph_i_cattributes_t *at = graph->attr;
1538
121k
    switch (type) {
1539
0
    case IGRAPH_ATTRIBUTE_GRAPH:
1540
0
        return igraph_i_cattribute_find_index(&at->gal, name) >= 0;
1541
76.4k
    case IGRAPH_ATTRIBUTE_VERTEX:
1542
76.4k
        return igraph_i_cattribute_find_index(&at->val, name) >= 0;
1543
44.5k
    case IGRAPH_ATTRIBUTE_EDGE:
1544
44.5k
        return igraph_i_cattribute_find_index(&at->eal, name) >= 0;
1545
0
    default:
1546
0
        IGRAPH_ERROR("Unknown attribute element type.", IGRAPH_EINVAL);
1547
0
        break;
1548
121k
    }
1549
1550
0
    return false;
1551
121k
}
1552
1553
static igraph_error_t igraph_i_cattribute_get_type(const igraph_t *graph,
1554
                                       igraph_attribute_type_t *type,
1555
                                       igraph_attribute_elemtype_t elemtype,
1556
4.49k
                                       const char *name) {
1557
4.49k
    igraph_attribute_record_t *rec;
1558
4.49k
    igraph_i_cattributes_t *at = graph->attr;
1559
4.49k
    igraph_attribute_record_list_t *al;
1560
1561
4.49k
    switch (elemtype) {
1562
0
    case IGRAPH_ATTRIBUTE_GRAPH:
1563
0
        al = &at->gal;
1564
0
        break;
1565
3.35k
    case IGRAPH_ATTRIBUTE_VERTEX:
1566
3.35k
        al = &at->val;
1567
3.35k
        break;
1568
1.13k
    case IGRAPH_ATTRIBUTE_EDGE:
1569
1.13k
        al = &at->eal;
1570
1.13k
        break;
1571
0
    default:
1572
0
        IGRAPH_ERROR("Unknown attribute element type.", IGRAPH_EINVAL);
1573
0
        break;
1574
4.49k
    }
1575
1576
4.49k
    IGRAPH_CHECK(igraph_i_cattribute_find_or_return(al, name, IGRAPH_ATTRIBUTE_UNSPECIFIED, &rec));
1577
4.49k
    *type = rec->type;
1578
1579
4.49k
    return IGRAPH_SUCCESS;
1580
4.49k
}
1581
1582
static igraph_error_t igraph_i_cattribute_get_numeric_graph_attr(
1583
    const igraph_t *graph, const char *name, igraph_vector_t *value
1584
0
) {
1585
0
    igraph_i_cattributes_t *attr = graph->attr;
1586
0
    igraph_attribute_record_list_t *gal = &attr->gal;
1587
0
    igraph_attribute_record_t *rec;
1588
1589
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_return(gal, name, IGRAPH_ATTRIBUTE_NUMERIC, &rec));
1590
0
    IGRAPH_CHECK(igraph_vector_push_back(value, VECTOR(*rec->value.as_vector)[0]));
1591
1592
0
    return IGRAPH_SUCCESS;
1593
0
}
1594
1595
static igraph_error_t igraph_i_cattribute_get_bool_graph_attr(
1596
    const igraph_t *graph, const char *name, igraph_vector_bool_t *value
1597
0
) {
1598
0
    igraph_i_cattributes_t *attr = graph->attr;
1599
0
    igraph_attribute_record_list_t *gal = &attr->gal;
1600
0
    igraph_attribute_record_t *rec;
1601
1602
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_return(gal, name, IGRAPH_ATTRIBUTE_BOOLEAN, &rec));
1603
0
    IGRAPH_CHECK(igraph_vector_bool_push_back(value, VECTOR(*rec->value.as_vector_bool)[0]));
1604
1605
0
    return IGRAPH_SUCCESS;
1606
0
}
1607
1608
static igraph_error_t igraph_i_cattribute_get_string_graph_attr(
1609
    const igraph_t *graph, const char *name, igraph_strvector_t *value
1610
0
) {
1611
0
    igraph_i_cattributes_t *attr = graph->attr;
1612
0
    igraph_attribute_record_list_t *gal = &attr->gal;
1613
0
    igraph_attribute_record_t *rec;
1614
1615
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_return(gal, name, IGRAPH_ATTRIBUTE_STRING, &rec));
1616
0
    IGRAPH_CHECK(igraph_strvector_push_back(value, igraph_strvector_get(rec->value.as_strvector, 0)));
1617
1618
0
    return IGRAPH_SUCCESS;
1619
0
}
1620
1621
static igraph_error_t igraph_i_cattribute_get_numeric_vertex_attr(const igraph_t *graph,
1622
                                                       const char *name,
1623
                                                       igraph_vs_t vs,
1624
3.01M
                                                       igraph_vector_t *value) {
1625
3.01M
    igraph_i_cattributes_t *attr = graph->attr;
1626
3.01M
    igraph_attribute_record_list_t *val = &attr->val;
1627
3.01M
    igraph_attribute_record_t *rec;
1628
3.01M
    const igraph_vector_t *num;
1629
1630
3.01M
    IGRAPH_CHECK(igraph_i_cattribute_find_or_return(val, name, IGRAPH_ATTRIBUTE_NUMERIC, &rec));
1631
1632
3.01M
    num = rec->value.as_vector;
1633
3.01M
    if (igraph_vs_is_all(&vs)) {
1634
0
        IGRAPH_CHECK(igraph_vector_append(value, num));
1635
3.01M
    } else {
1636
3.01M
        igraph_vit_t it;
1637
3.01M
        igraph_int_t i = igraph_vector_size(value);
1638
3.01M
        IGRAPH_CHECK(igraph_vit_create(graph, vs, &it));
1639
3.01M
        IGRAPH_FINALLY(igraph_vit_destroy, &it);
1640
3.01M
        IGRAPH_CHECK(igraph_vector_resize(value, i + IGRAPH_VIT_SIZE(it)));
1641
6.02M
        for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) {
1642
3.01M
            igraph_int_t v = IGRAPH_VIT_GET(it);
1643
3.01M
            VECTOR(*value)[i] = VECTOR(*num)[v];
1644
3.01M
        }
1645
3.01M
        igraph_vit_destroy(&it);
1646
3.01M
        IGRAPH_FINALLY_CLEAN(1);
1647
3.01M
    }
1648
1649
3.01M
    return IGRAPH_SUCCESS;
1650
3.01M
}
1651
1652
static igraph_error_t igraph_i_cattribute_get_bool_vertex_attr(const igraph_t *graph,
1653
                                                    const char *name,
1654
                                                    igraph_vs_t vs,
1655
20.2M
                                                    igraph_vector_bool_t *value) {
1656
20.2M
    igraph_i_cattributes_t *attr = graph->attr;
1657
20.2M
    igraph_attribute_record_list_t *val = &attr->val;
1658
20.2M
    igraph_attribute_record_t *rec;
1659
20.2M
    const igraph_vector_bool_t *log;
1660
1661
20.2M
    IGRAPH_CHECK(igraph_i_cattribute_find_or_return(val, name, IGRAPH_ATTRIBUTE_BOOLEAN, &rec));
1662
1663
20.2M
    log = rec->value.as_vector_bool;
1664
20.2M
    if (igraph_vs_is_all(&vs)) {
1665
0
        IGRAPH_CHECK(igraph_vector_bool_append(value, log));
1666
20.2M
    } else {
1667
20.2M
        igraph_vit_t it;
1668
20.2M
        igraph_int_t i = igraph_vector_bool_size(value);
1669
20.2M
        IGRAPH_CHECK(igraph_vit_create(graph, vs, &it));
1670
20.2M
        IGRAPH_FINALLY(igraph_vit_destroy, &it);
1671
20.2M
        IGRAPH_CHECK(igraph_vector_bool_resize(value, i + IGRAPH_VIT_SIZE(it)));
1672
40.4M
        for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) {
1673
20.2M
            igraph_int_t v = IGRAPH_VIT_GET(it);
1674
20.2M
            VECTOR(*value)[i] = VECTOR(*log)[v];
1675
20.2M
        }
1676
20.2M
        igraph_vit_destroy(&it);
1677
20.2M
        IGRAPH_FINALLY_CLEAN(1);
1678
20.2M
    }
1679
1680
20.2M
    return IGRAPH_SUCCESS;
1681
20.2M
}
1682
1683
static igraph_error_t igraph_i_cattribute_get_string_vertex_attr(const igraph_t *graph,
1684
                                                      const char *name,
1685
                                                      igraph_vs_t vs,
1686
3.50M
                                                      igraph_strvector_t *value) {
1687
3.50M
    igraph_i_cattributes_t *attr = graph->attr;
1688
3.50M
    igraph_attribute_record_list_t *val = &attr->val;
1689
3.50M
    igraph_attribute_record_t *rec;
1690
3.50M
    const igraph_strvector_t *str;
1691
1692
3.50M
    IGRAPH_CHECK(igraph_i_cattribute_find_or_return(val, name, IGRAPH_ATTRIBUTE_STRING, &rec));
1693
1694
3.50M
    str = rec->value.as_strvector;
1695
3.50M
    if (igraph_vs_is_all(&vs)) {
1696
0
        igraph_strvector_clear(value);
1697
0
        IGRAPH_CHECK(igraph_strvector_append(value, str));
1698
3.50M
    } else {
1699
3.50M
        igraph_vit_t it;
1700
3.50M
        igraph_int_t i = igraph_strvector_size(value);
1701
3.50M
        IGRAPH_CHECK(igraph_vit_create(graph, vs, &it));
1702
3.50M
        IGRAPH_FINALLY(igraph_vit_destroy, &it);
1703
3.50M
        IGRAPH_CHECK(igraph_strvector_resize(value, i + IGRAPH_VIT_SIZE(it)));
1704
7.01M
        for (; !IGRAPH_VIT_END(it); IGRAPH_VIT_NEXT(it), i++) {
1705
3.50M
            igraph_int_t v = IGRAPH_VIT_GET(it);
1706
3.50M
            IGRAPH_CHECK(igraph_strvector_set(value, i, igraph_strvector_get(str, v)));
1707
3.50M
        }
1708
3.50M
        igraph_vit_destroy(&it);
1709
3.50M
        IGRAPH_FINALLY_CLEAN(1);
1710
3.50M
    }
1711
1712
3.50M
    return IGRAPH_SUCCESS;
1713
3.50M
}
1714
1715
static igraph_error_t igraph_i_cattribute_get_numeric_edge_attr(
1716
    const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_t *value
1717
3.54M
) {
1718
3.54M
    igraph_i_cattributes_t *attr = graph->attr;
1719
3.54M
    igraph_attribute_record_list_t *eal = &attr->eal;
1720
3.54M
    igraph_attribute_record_t *rec;
1721
3.54M
    const igraph_vector_t *num;
1722
1723
3.54M
    IGRAPH_CHECK(igraph_i_cattribute_find_or_return(eal, name, IGRAPH_ATTRIBUTE_NUMERIC, &rec));
1724
1725
3.54M
    num = rec->value.as_vector;
1726
3.54M
    if (igraph_es_is_all(&es)) {
1727
0
        IGRAPH_CHECK(igraph_vector_append(value, num));
1728
3.54M
    } else {
1729
3.54M
        igraph_eit_t it;
1730
3.54M
        igraph_int_t i = igraph_vector_size(value);
1731
3.54M
        IGRAPH_CHECK(igraph_eit_create(graph, es, &it));
1732
3.54M
        IGRAPH_FINALLY(igraph_eit_destroy, &it);
1733
3.54M
        IGRAPH_CHECK(igraph_vector_resize(value, i + IGRAPH_EIT_SIZE(it)));
1734
7.09M
        for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) {
1735
3.54M
            igraph_int_t e = IGRAPH_EIT_GET(it);
1736
3.54M
            VECTOR(*value)[i] = VECTOR(*num)[e];
1737
3.54M
        }
1738
3.54M
        igraph_eit_destroy(&it);
1739
3.54M
        IGRAPH_FINALLY_CLEAN(1);
1740
3.54M
    }
1741
1742
3.54M
    return IGRAPH_SUCCESS;
1743
3.54M
}
1744
1745
static igraph_error_t igraph_i_cattribute_get_string_edge_attr(
1746
    const igraph_t *graph, const char *name, igraph_es_t es,
1747
    igraph_strvector_t *value
1748
726k
) {
1749
726k
    igraph_i_cattributes_t *attr = graph->attr;
1750
726k
    igraph_attribute_record_list_t *eal = &attr->eal;
1751
726k
    igraph_attribute_record_t *rec;
1752
726k
    const igraph_strvector_t *str;
1753
1754
726k
    IGRAPH_CHECK(igraph_i_cattribute_find_or_return(eal, name, IGRAPH_ATTRIBUTE_STRING, &rec));
1755
1756
726k
    str = rec->value.as_strvector;
1757
726k
    if (igraph_es_is_all(&es)) {
1758
0
        IGRAPH_CHECK(igraph_strvector_append(value, str));
1759
726k
    } else {
1760
726k
        igraph_eit_t it;
1761
726k
        igraph_int_t i = igraph_strvector_size(value);
1762
726k
        IGRAPH_CHECK(igraph_eit_create(graph, es, &it));
1763
726k
        IGRAPH_FINALLY(igraph_eit_destroy, &it);
1764
726k
        IGRAPH_CHECK(igraph_strvector_resize(value, i + IGRAPH_EIT_SIZE(it)));
1765
1.45M
        for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) {
1766
726k
            igraph_int_t e = IGRAPH_EIT_GET(it);
1767
726k
            IGRAPH_CHECK(igraph_strvector_set(value, i, igraph_strvector_get(str, e)));
1768
726k
        }
1769
726k
        igraph_eit_destroy(&it);
1770
726k
        IGRAPH_FINALLY_CLEAN(1);
1771
726k
    }
1772
1773
726k
    return IGRAPH_SUCCESS;
1774
726k
}
1775
1776
static igraph_error_t igraph_i_cattribute_get_bool_edge_attr(
1777
    const igraph_t *graph, const char *name, igraph_es_t es,
1778
    igraph_vector_bool_t *value
1779
0
) {
1780
0
    igraph_i_cattributes_t *attr = graph->attr;
1781
0
    igraph_attribute_record_list_t *eal = &attr->eal;
1782
0
    igraph_attribute_record_t *rec;
1783
0
    const igraph_vector_bool_t *log;
1784
1785
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_return(eal, name, IGRAPH_ATTRIBUTE_BOOLEAN, &rec));
1786
1787
0
    log = rec->value.as_vector_bool;
1788
0
    if (igraph_es_is_all(&es)) {
1789
0
        IGRAPH_CHECK(igraph_vector_bool_append(value, log));
1790
0
    } else {
1791
0
        igraph_eit_t it;
1792
0
        igraph_int_t i = igraph_vector_bool_size(value);
1793
0
        IGRAPH_CHECK(igraph_eit_create(graph, es, &it));
1794
0
        IGRAPH_FINALLY(igraph_eit_destroy, &it);
1795
0
        IGRAPH_CHECK(igraph_vector_bool_resize(value, i + IGRAPH_EIT_SIZE(it)));
1796
0
        for (; !IGRAPH_EIT_END(it); IGRAPH_EIT_NEXT(it), i++) {
1797
0
            igraph_int_t e = IGRAPH_EIT_GET(it);
1798
0
            VECTOR(*value)[i] = VECTOR(*log)[e];
1799
0
        }
1800
0
        igraph_eit_destroy(&it);
1801
0
        IGRAPH_FINALLY_CLEAN(1);
1802
0
    }
1803
1804
0
    return IGRAPH_SUCCESS;
1805
0
}
1806
1807
/* -------------------------------------- */
1808
1809
const igraph_attribute_table_t igraph_cattribute_table = {
1810
    &igraph_i_cattribute_init,
1811
    &igraph_i_cattribute_destroy,
1812
    &igraph_i_cattribute_copy,
1813
    &igraph_i_cattribute_add_vertices,
1814
    &igraph_i_cattribute_permute_vertices,
1815
    &igraph_i_cattribute_combine_vertices,
1816
    &igraph_i_cattribute_add_edges,
1817
    &igraph_i_cattribute_permute_edges,
1818
    &igraph_i_cattribute_combine_edges,
1819
    &igraph_i_cattribute_get_info,
1820
    &igraph_i_cattribute_has_attr,
1821
    &igraph_i_cattribute_get_type,
1822
    &igraph_i_cattribute_get_numeric_graph_attr,
1823
    &igraph_i_cattribute_get_string_graph_attr,
1824
    &igraph_i_cattribute_get_bool_graph_attr,
1825
    &igraph_i_cattribute_get_numeric_vertex_attr,
1826
    &igraph_i_cattribute_get_string_vertex_attr,
1827
    &igraph_i_cattribute_get_bool_vertex_attr,
1828
    &igraph_i_cattribute_get_numeric_edge_attr,
1829
    &igraph_i_cattribute_get_string_edge_attr,
1830
    &igraph_i_cattribute_get_bool_edge_attr
1831
};
1832
1833
/* -------------------------------------- */
1834
1835
/**
1836
 * \section cattributes
1837
 * <para>There is an experimental attribute handler that can be used
1838
 * from C code. In this section we show how this works. This attribute
1839
 * handler is by default not attached (the default is no attribute
1840
 * handler), so we first need to attach it:
1841
 * <programlisting>
1842
 * igraph_set_attribute_table(&amp;igraph_cattribute_table);
1843
 * </programlisting>
1844
 * </para>
1845
 * <para>Now the attribute functions are available. Please note that
1846
 * the attribute handler must be attached before you call any other
1847
 * igraph functions, otherwise you might end up with graphs without
1848
 * attributes and an active attribute handler, which might cause
1849
 * unexpected program behaviour. The rule is that you attach the
1850
 * attribute handler in the beginning of your
1851
 * <function>main()</function> and never touch it again. Detaching
1852
 * the attribute handler might lead to memory leaks.</para>
1853
 *
1854
 * <para>It is not currently possible to have attribute handlers on a
1855
 * per-graph basis. All graphs in an application must be managed with
1856
 * the same attribute handler. This also applies to the default case
1857
 * when there is no attribute handler at all.</para>
1858
 *
1859
 * <para>The C attribute handler supports attaching real numbers, boolean
1860
 * values and character strings as attributes. No vector values are allowed.
1861
 * For example, vertices have a <code>name</code> attribute holding a single
1862
 * string value for each vertex, but it is not possible to have a <code>coords</code>
1863
 * attribute which is a vector of numbers per vertex.</para>
1864
 *
1865
 * <para>The functions documented in this section are specific to the C
1866
 * attribute handler. Code using these functions will not function when
1867
 * a different attribute handler is attached.</para>
1868
 *
1869
 * \example examples/simple/cattributes.c
1870
 * \example examples/simple/cattributes2.c
1871
 * \example examples/simple/cattributes3.c
1872
 * \example examples/simple/cattributes4.c
1873
 */
1874
1875
/**
1876
 * \function igraph_cattribute_GAN
1877
 * \brief Query a numeric graph attribute.
1878
 *
1879
 * Returns the value of the given numeric graph attribute.
1880
 * If the attribute does not exist, a warning is issued
1881
 * and NaN is returned.
1882
 *
1883
 * \param graph The input graph.
1884
 * \param name The name of the attribute to query.
1885
 * \return The value of the attribute.
1886
 *
1887
 * \sa \ref GAN for a simpler interface.
1888
 *
1889
 * Time complexity: O(Ag), the number of graph attributes.
1890
 */
1891
0
igraph_real_t igraph_cattribute_GAN(const igraph_t *graph, const char *name) {
1892
0
    igraph_i_cattributes_t *attr = graph->attr;
1893
0
    const igraph_attribute_record_t *rec;
1894
0
    const igraph_vector_t *num;
1895
1896
0
    rec = igraph_i_cattribute_find(&attr->gal, name, IGRAPH_ATTRIBUTE_NUMERIC);
1897
0
    if (!rec) {
1898
0
        IGRAPH_WARNINGF("Graph attribute '%s' does not exist, returning default numeric attribute value.", name);
1899
0
        return IGRAPH_NAN;
1900
0
    }
1901
1902
0
    num = rec->value.as_vector;
1903
0
    return VECTOR(*num)[0];
1904
0
}
1905
1906
/**
1907
 * \function igraph_cattribute_GAB
1908
 * \brief Query a boolean graph attribute.
1909
 *
1910
 * Returns the value of the given boolean graph attribute.
1911
 * If the attribute does not exist, a warning is issued
1912
 * and false is returned.
1913
 *
1914
 * \param graph The input graph.
1915
 * \param name The name of the attribute to query.
1916
 * \return The value of the attribute.
1917
 *
1918
 * \sa \ref GAB for a simpler interface.
1919
 *
1920
 * Time complexity: O(Ag), the number of graph attributes.
1921
 */
1922
0
igraph_bool_t igraph_cattribute_GAB(const igraph_t *graph, const char *name) {
1923
0
    igraph_i_cattributes_t *attr = graph->attr;
1924
0
    const igraph_attribute_record_t *rec;
1925
0
    const igraph_vector_bool_t *log;
1926
1927
0
    rec = igraph_i_cattribute_find(&attr->gal, name, IGRAPH_ATTRIBUTE_BOOLEAN);
1928
0
    if (!rec) {
1929
0
        IGRAPH_WARNINGF("Graph attribute '%s' does not exist, returning default boolean attribute value.", name);
1930
0
        return false;
1931
0
    }
1932
1933
0
    log = rec->value.as_vector_bool;
1934
0
    return VECTOR(*log)[0];
1935
0
}
1936
1937
/**
1938
 * \function igraph_cattribute_GAS
1939
 * \brief Query a string graph attribute.
1940
 *
1941
 * Returns a <type>const</type> pointer to the string graph attribute
1942
 * specified in \p name. The value must not be modified.
1943
 * If the attribute does not exist, a warning is issued and
1944
 * an empty string is returned.
1945
 *
1946
 * \param graph The input graph.
1947
 * \param name The name of the attribute to query.
1948
 * \return The value of the attribute.
1949
 *
1950
 * \sa \ref GAS for a simpler interface.
1951
 *
1952
 * Time complexity: O(Ag), the number of graph attributes.
1953
 */
1954
0
const char *igraph_cattribute_GAS(const igraph_t *graph, const char *name) {
1955
0
    igraph_i_cattributes_t *attr = graph->attr;
1956
0
    const igraph_attribute_record_t *rec;
1957
0
    const igraph_strvector_t *str;
1958
1959
0
    rec = igraph_i_cattribute_find(&attr->gal, name, IGRAPH_ATTRIBUTE_STRING);
1960
0
    if (!rec) {
1961
0
        IGRAPH_WARNINGF("Graph attribute '%s' does not exist, returning default string attribute value.", name);
1962
0
        return "";
1963
0
    }
1964
1965
0
    str = rec->value.as_strvector;
1966
0
    return igraph_strvector_get(str, 0);
1967
0
}
1968
1969
/**
1970
 * \function igraph_cattribute_VAN
1971
 * \brief Query a numeric vertex attribute.
1972
 *
1973
 * If the attribute does not exist, a warning is issued and
1974
 * NaN is returned. See \ref igraph_cattribute_VANV() for
1975
 * an error-checked version.
1976
 *
1977
 * \param graph The input graph.
1978
 * \param name The name of the attribute.
1979
 * \param vid The id of the queried vertex.
1980
 * \return The value of the attribute.
1981
 *
1982
 * \sa \ref VAN macro for a simpler interface.
1983
 *
1984
 * Time complexity: O(Av), the number of vertex attributes.
1985
 */
1986
igraph_real_t igraph_cattribute_VAN(const igraph_t *graph, const char *name,
1987
0
                                    igraph_int_t vid) {
1988
0
    igraph_i_cattributes_t *attr = graph->attr;
1989
0
    const igraph_attribute_record_t *rec;
1990
0
    const igraph_vector_t *num;
1991
1992
0
    rec = igraph_i_cattribute_find(&attr->val, name, IGRAPH_ATTRIBUTE_NUMERIC);
1993
0
    if (!rec) {
1994
0
        IGRAPH_WARNINGF("Vertex attribute '%s' does not exist, returning default numeric attribute value.", name);
1995
0
        return IGRAPH_NAN;
1996
0
    }
1997
1998
0
    num = rec->value.as_vector;
1999
0
    return VECTOR(*num)[vid];
2000
0
}
2001
2002
/**
2003
 * \function igraph_cattribute_VAB
2004
 * \brief Query a boolean vertex attribute.
2005
 *
2006
 * If the vertex attribute does not exist, a warning is issued
2007
 * and false is returned. See \ref igraph_cattribute_VABV() for
2008
 * an error-checked version.
2009
 *
2010
 * \param graph The input graph.
2011
 * \param name The name of the attribute.
2012
 * \param vid The id of the queried vertex.
2013
 * \return The value of the attribute.
2014
 *
2015
 * \sa \ref VAB macro for a simpler interface.
2016
 *
2017
 * Time complexity: O(Av), the number of vertex attributes.
2018
 */
2019
igraph_bool_t igraph_cattribute_VAB(const igraph_t *graph, const char *name,
2020
0
                                    igraph_int_t vid) {
2021
0
    igraph_i_cattributes_t *attr = graph->attr;
2022
0
    const igraph_attribute_record_t *rec;
2023
0
    const igraph_vector_bool_t *log;
2024
2025
0
    rec = igraph_i_cattribute_find(&attr->val, name, IGRAPH_ATTRIBUTE_BOOLEAN);
2026
0
    if (!rec) {
2027
0
        IGRAPH_WARNINGF("Vertex attribute '%s' does not exist, returning default boolean attribute value.", name);
2028
0
        return false;
2029
0
    }
2030
2031
0
    log = rec->value.as_vector_bool;
2032
0
    return VECTOR(*log)[vid];
2033
0
}
2034
2035
/**
2036
 * \function igraph_cattribute_VAS
2037
 * \brief Query a string vertex attribute.
2038
 *
2039
 * Returns a <type>const</type> pointer to the string vertex attribute
2040
 * specified in \p name. The value must not be modified.
2041
 * If the vertex attribute does not exist, a warning is issued and
2042
 * an empty string is returned. See \ref igraph_cattribute_VASV()
2043
 * for an error-checked version.
2044
 *
2045
 * \param graph The input graph.
2046
 * \param name The name of the attribute.
2047
 * \param vid The id of the queried vertex.
2048
 * \return The value of the attribute.
2049
 *
2050
 * \sa The macro \ref VAS for a simpler interface.
2051
 *
2052
 * Time complexity: O(Av), the number of vertex attributes.
2053
 */
2054
const char *igraph_cattribute_VAS(const igraph_t *graph, const char *name,
2055
0
                                  igraph_int_t vid) {
2056
0
    igraph_i_cattributes_t *attr = graph->attr;
2057
0
    const igraph_attribute_record_t *rec;
2058
0
    const igraph_strvector_t *str;
2059
2060
0
    rec = igraph_i_cattribute_find(&attr->val, name, IGRAPH_ATTRIBUTE_STRING);
2061
0
    if (!rec) {
2062
0
        IGRAPH_WARNINGF("Vertex attribute '%s' does not exist, returning default string attribute value.", name);
2063
0
        return "";
2064
0
    }
2065
2066
0
    str = rec->value.as_strvector;
2067
0
    return igraph_strvector_get(str, vid);
2068
0
}
2069
2070
/**
2071
 * \function igraph_cattribute_EAN
2072
 * \brief Query a numeric edge attribute.
2073
 *
2074
 * If the attribute does not exist, a warning is issued and
2075
 * NaN is returned. See \ref igraph_cattribute_EANV() for
2076
 * an error-checked version.
2077
 *
2078
 * \param graph The input graph.
2079
 * \param name The name of the attribute.
2080
 * \param eid The id of the queried edge.
2081
 * \return The value of the attribute.
2082
 *
2083
 * \sa \ref EAN for an easier interface.
2084
 *
2085
 * Time complexity: O(Ae), the number of edge attributes.
2086
 */
2087
igraph_real_t igraph_cattribute_EAN(const igraph_t *graph, const char *name,
2088
0
                                    igraph_int_t eid) {
2089
0
    igraph_i_cattributes_t *attr = graph->attr;
2090
0
    const igraph_attribute_record_t *rec;
2091
0
    const igraph_vector_t *num;
2092
2093
0
    rec = igraph_i_cattribute_find(&attr->eal, name, IGRAPH_ATTRIBUTE_NUMERIC);
2094
0
    if (!rec) {
2095
0
        IGRAPH_WARNINGF("Edge attribute '%s' does not exist, returning default numeric attribute value.", name);
2096
0
        return IGRAPH_NAN;
2097
0
    }
2098
2099
0
    num = rec->value.as_vector;
2100
0
    return VECTOR(*num)[eid];
2101
0
}
2102
2103
/**
2104
 * \function igraph_cattribute_EAB
2105
 * \brief Query a boolean edge attribute.
2106
 *
2107
 * If the edge attribute does not exist, a warning is issued and
2108
 * false is returned. See \ref igraph_cattribute_EABV() for
2109
 * an error-checked version.
2110
 *
2111
 * \param graph The input graph.
2112
 * \param name The name of the attribute.
2113
 * \param eid The id of the queried edge.
2114
 * \return The value of the attribute.
2115
 *
2116
 * \sa \ref EAB for an easier interface.
2117
 *
2118
 * Time complexity: O(Ae), the number of edge attributes.
2119
 */
2120
igraph_bool_t igraph_cattribute_EAB(const igraph_t *graph, const char *name,
2121
0
                                    igraph_int_t eid) {
2122
0
    igraph_i_cattributes_t *attr = graph->attr;
2123
0
    const igraph_attribute_record_t *rec;
2124
0
    const igraph_vector_bool_t *log;
2125
2126
0
    rec = igraph_i_cattribute_find(&attr->eal, name, IGRAPH_ATTRIBUTE_BOOLEAN);
2127
0
    if (!rec) {
2128
0
        IGRAPH_WARNINGF("Edge attribute '%s' does not exist, returning default boolean attribute value.", name);
2129
0
        return false;
2130
0
    }
2131
2132
0
    log = rec->value.as_vector_bool;
2133
0
    return VECTOR(*log)[eid];
2134
0
}
2135
2136
/**
2137
 * \function igraph_cattribute_EAS
2138
 * \brief Query a string edge attribute.
2139
 *
2140
 * Returns a <type>const</type> pointer to the string edge attribute
2141
 * specified in \p name. The value must not be modified.
2142
 * If the edge attribute does not exist, a warning is issued and
2143
 * an empty string is returned. See \ref igraph_cattribute_EASV() for
2144
 * an error-checked version.
2145
 *
2146
 * \param graph The input graph.
2147
 * \param name The name of the attribute.
2148
 * \param eid The id of the queried edge.
2149
 * \return The value of the attribute.
2150
 *
2151
 * \se \ref EAS if you want to type less.
2152
 *
2153
 * Time complexity: O(Ae), the number of edge attributes.
2154
 */
2155
const char *igraph_cattribute_EAS(const igraph_t *graph, const char *name,
2156
0
                                  igraph_int_t eid) {
2157
0
    igraph_i_cattributes_t *attr = graph->attr;
2158
0
    const igraph_attribute_record_t *rec;
2159
0
    const igraph_strvector_t *str;
2160
2161
0
    rec = igraph_i_cattribute_find(&attr->eal, name, IGRAPH_ATTRIBUTE_STRING);
2162
0
    if (!rec) {
2163
0
        IGRAPH_WARNINGF("Edge attribute '%s' does not exist, returning default string attribute value.", name);
2164
0
        return "";
2165
0
    }
2166
2167
0
    str = rec->value.as_strvector;
2168
0
    return igraph_strvector_get(str, eid);
2169
0
}
2170
2171
/**
2172
 * \function igraph_cattribute_VANV
2173
 * \brief Query a numeric vertex attribute for many vertices.
2174
 *
2175
 * \param graph The input graph.
2176
 * \param name The name of the attribute.
2177
 * \param vids The vertices to query.
2178
 * \param result Pointer to an initialized vector, the result is
2179
 *    stored here. It will be resized, if needed.
2180
 * \return Error code.
2181
 *
2182
 * Time complexity: O(v), where v is the number of vertices in 'vids'.
2183
 */
2184
2185
igraph_error_t igraph_cattribute_VANV(const igraph_t *graph, const char *name,
2186
0
                           igraph_vs_t vids, igraph_vector_t *result) {
2187
0
    igraph_vector_clear(result);
2188
0
    return igraph_i_cattribute_get_numeric_vertex_attr(graph, name, vids, result);
2189
0
}
2190
2191
/**
2192
 * \function igraph_cattribute_VABV
2193
 * \brief Query a boolean vertex attribute for many vertices.
2194
 *
2195
 * \param graph The input graph.
2196
 * \param name The name of the attribute.
2197
 * \param vids The vertices to query.
2198
 * \param result Pointer to an initialized boolean vector, the result is
2199
 *    stored here. It will be resized, if needed.
2200
 * \return Error code.
2201
 *
2202
 * Time complexity: O(v), where v is the number of vertices in 'vids'.
2203
 */
2204
2205
igraph_error_t igraph_cattribute_VABV(const igraph_t *graph, const char *name,
2206
0
                           igraph_vs_t vids, igraph_vector_bool_t *result) {
2207
0
    igraph_vector_bool_clear(result);
2208
0
    return igraph_i_cattribute_get_bool_vertex_attr(graph, name, vids, result);
2209
0
}
2210
2211
/**
2212
 * \function igraph_cattribute_EANV
2213
 * \brief Query a numeric edge attribute for many edges.
2214
 *
2215
 * \param graph The input graph.
2216
 * \param name The name of the attribute.
2217
 * \param eids The edges to query.
2218
 * \param result Pointer to an initialized vector, the result is
2219
 *    stored here. It will be resized, if needed.
2220
 * \return Error code.
2221
 *
2222
 * Time complexity: O(e), where e is the number of edges in 'eids'.
2223
 */
2224
2225
igraph_error_t igraph_cattribute_EANV(const igraph_t *graph, const char *name,
2226
0
                           igraph_es_t eids, igraph_vector_t *result) {
2227
0
    igraph_vector_clear(result);
2228
0
    return igraph_i_cattribute_get_numeric_edge_attr(graph, name, eids, result);
2229
0
}
2230
2231
/**
2232
 * \function igraph_cattribute_EABV
2233
 * \brief Query a boolean edge attribute for many edges.
2234
 *
2235
 * \param graph The input graph.
2236
 * \param name The name of the attribute.
2237
 * \param eids The edges to query.
2238
 * \param result Pointer to an initialized boolean vector, the result is
2239
 *    stored here. It will be resized, if needed.
2240
 * \return Error code.
2241
 *
2242
 * Time complexity: O(e), where e is the number of edges in 'eids'.
2243
 */
2244
2245
igraph_error_t igraph_cattribute_EABV(const igraph_t *graph, const char *name,
2246
0
                           igraph_es_t eids, igraph_vector_bool_t *result) {
2247
0
    igraph_vector_bool_clear(result);
2248
0
    return igraph_i_cattribute_get_bool_edge_attr(graph, name, eids, result);
2249
0
}
2250
2251
/**
2252
 * \function igraph_cattribute_VASV
2253
 * \brief Query a string vertex attribute for many vertices.
2254
 *
2255
 * \param graph The input graph.
2256
 * \param name The name of the attribute.
2257
 * \param vids The vertices to query.
2258
 * \param result Pointer to an initialized string vector, the result
2259
 *     is stored here. It will be resized, if needed.
2260
 * \return Error code.
2261
 *
2262
 * Time complexity: O(v), where v is the number of vertices in 'vids'.
2263
 * (We assume that the string attributes have a bounded length.)
2264
 */
2265
2266
igraph_error_t igraph_cattribute_VASV(const igraph_t *graph, const char *name,
2267
0
                           igraph_vs_t vids, igraph_strvector_t *result) {
2268
0
    igraph_strvector_clear(result);
2269
0
    return igraph_i_cattribute_get_string_vertex_attr(graph, name, vids, result);
2270
0
}
2271
2272
/**
2273
 * \function igraph_cattribute_EASV
2274
 * \brief Query a string edge attribute for many edges.
2275
 *
2276
 * \param graph The input graph.
2277
 * \param name The name of the attribute.
2278
 * \param eids The edges to query.
2279
 * \param result Pointer to an initialized string vector, the result
2280
 *     is stored here. It will be resized, if needed.
2281
 * \return Error code.
2282
 *
2283
 * Time complexity: O(e), where e is the number of edges in
2284
 * 'eids'. (We assume that the string attributes have a bounded length.)
2285
 */
2286
2287
igraph_error_t igraph_cattribute_EASV(const igraph_t *graph, const char *name,
2288
0
                           igraph_es_t eids, igraph_strvector_t *result) {
2289
0
    igraph_strvector_clear(result);
2290
0
    return igraph_i_cattribute_get_string_edge_attr(graph, name, eids, result);
2291
0
}
2292
2293
/**
2294
 * \function igraph_cattribute_list
2295
 * \brief List all attributes.
2296
 *
2297
 * See \ref igraph_attribute_type_t for the various attribute types.
2298
 * \param graph The input graph.
2299
 * \param gnames String vector, the names of the graph attributes.
2300
 * \param gtypes Numeric vector, the types of the graph attributes.
2301
 * \param vnames String vector, the names of the vertex attributes.
2302
 * \param vtypes Numeric vector, the types of the vertex attributes.
2303
 * \param enames String vector, the names of the edge attributes.
2304
 * \param etypes Numeric vector, the types of the edge attributes.
2305
 * \return Error code.
2306
 *
2307
 * Naturally, the string vector with the attribute names and the
2308
 * numeric vector with the attribute types are in the right order,
2309
 * i.e. the first name corresponds to the first type, etc.
2310
 *
2311
 * Time complexity: O(Ag+Av+Ae), the number of all attributes.
2312
 */
2313
igraph_error_t igraph_cattribute_list(const igraph_t *graph,
2314
                           igraph_strvector_t *gnames, igraph_vector_int_t *gtypes,
2315
                           igraph_strvector_t *vnames, igraph_vector_int_t *vtypes,
2316
0
                           igraph_strvector_t *enames, igraph_vector_int_t *etypes) {
2317
0
    return igraph_i_cattribute_get_info(graph, gnames, gtypes, vnames, vtypes,
2318
0
                                        enames, etypes);
2319
0
}
2320
2321
/**
2322
 * \function igraph_cattribute_has_attr
2323
 * \brief Checks whether a (graph, vertex or edge) attribute exists.
2324
 *
2325
 * \param graph The graph.
2326
 * \param type The type of the attribute, \c IGRAPH_ATTRIBUTE_GRAPH,
2327
 *        \c IGRAPH_ATTRIBUTE_VERTEX or \c IGRAPH_ATTRIBUTE_EDGE.
2328
 * \param name Character constant, the name of the attribute.
2329
 * \return Boolean value, \c true if the attribute exists, \c false otherwise.
2330
 *
2331
 * Time complexity: O(A), the number of (graph, vertex or edge)
2332
 * attributes, assuming attribute names are not too long.
2333
 */
2334
igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph,
2335
        igraph_attribute_elemtype_t type,
2336
0
        const char *name) {
2337
0
    return igraph_i_cattribute_has_attr(graph, type, name);
2338
0
}
2339
2340
/**
2341
 * \function igraph_cattribute_GAN_set
2342
 * \brief Set a numeric graph attribute.
2343
 *
2344
 * \param graph The graph.
2345
 * \param name Name of the graph attribute. If there is no such
2346
 *   attribute yet, then it will be added.
2347
 * \param value The (new) value of the graph attribute.
2348
 * \return Error code.
2349
 *
2350
 * \se \ref SETGAN if you want to type less.
2351
 *
2352
 * Time complexity: O(1).
2353
 */
2354
igraph_error_t igraph_cattribute_GAN_set(igraph_t *graph, const char *name,
2355
0
                              igraph_real_t value) {
2356
0
    igraph_i_cattributes_t *attr = graph->attr;
2357
0
    igraph_attribute_record_t *rec;
2358
0
    igraph_vector_t *num;
2359
2360
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(
2361
0
        &attr->gal, name, IGRAPH_ATTRIBUTE_NUMERIC, 1, &rec
2362
0
    ));
2363
2364
0
    num = rec->value.as_vector;
2365
0
    VECTOR(*num)[0] = value;
2366
2367
0
    return IGRAPH_SUCCESS;
2368
0
}
2369
2370
/**
2371
 * \function igraph_cattribute_GAB_set
2372
 * \brief Set a boolean graph attribute.
2373
 *
2374
 * \param graph The graph.
2375
 * \param name Name of the graph attribute. If there is no such
2376
 *   attribute yet, then it will be added.
2377
 * \param value The (new) value of the graph attribute.
2378
 * \return Error code.
2379
 *
2380
 * \se \ref SETGAN if you want to type less.
2381
 *
2382
 * Time complexity: O(1).
2383
 */
2384
igraph_error_t igraph_cattribute_GAB_set(igraph_t *graph, const char *name,
2385
0
                              igraph_bool_t value) {
2386
0
    igraph_i_cattributes_t *attr = graph->attr;
2387
0
    igraph_attribute_record_t *rec;
2388
0
    igraph_vector_bool_t *log;
2389
2390
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(
2391
0
        &attr->gal, name, IGRAPH_ATTRIBUTE_BOOLEAN, 1, &rec
2392
0
    ));
2393
2394
0
    log = rec->value.as_vector_bool;
2395
0
    VECTOR(*log)[0] = value;
2396
2397
0
    return IGRAPH_SUCCESS;
2398
0
}
2399
2400
/**
2401
 * \function igraph_cattribute_GAS_set
2402
 * \brief Set a string graph attribute.
2403
 *
2404
 * \param graph The graph.
2405
 * \param name Name of the graph attribute. If there is no such
2406
 *   attribute yet, then it will be added.
2407
 * \param value The (new) value of the graph attribute. It will be
2408
 *   copied.
2409
 * \return Error code.
2410
 *
2411
 * \se \ref SETGAS if you want to type less.
2412
 *
2413
 * Time complexity: O(1).
2414
 */
2415
igraph_error_t igraph_cattribute_GAS_set(igraph_t *graph, const char *name,
2416
0
                              const char *value) {
2417
0
    igraph_i_cattributes_t *attr = graph->attr;
2418
0
    igraph_attribute_record_t *rec;
2419
0
    igraph_strvector_t *str;
2420
2421
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(
2422
0
        &attr->gal, name, IGRAPH_ATTRIBUTE_STRING, 1, &rec
2423
0
    ));
2424
2425
0
    str = rec->value.as_strvector;
2426
0
    IGRAPH_CHECK(igraph_strvector_set(str, 0, value));
2427
2428
0
    return IGRAPH_SUCCESS;
2429
0
}
2430
2431
/**
2432
 * \function igraph_cattribute_VAN_set
2433
 * \brief Set a numeric vertex attribute.
2434
 *
2435
 * The attribute will be added if not present already. If present it
2436
 * will be overwritten. The same \p value is set for all vertices
2437
 * included in \p vid.
2438
 * \param graph The graph.
2439
 * \param name Name of the attribute.
2440
 * \param vid Vertices for which to set the attribute.
2441
 * \param value The (new) value of the attribute.
2442
 * \return Error code.
2443
 *
2444
 * \sa \ref SETVAN for a simpler way.
2445
 *
2446
 * Time complexity: O(n), the number of vertices if the attribute is
2447
 * new, O(|vid|) otherwise.
2448
 */
2449
igraph_error_t igraph_cattribute_VAN_set(igraph_t *graph, const char *name,
2450
0
                              igraph_int_t vid, igraph_real_t value) {
2451
0
    igraph_i_cattributes_t *attr = graph->attr;
2452
0
    igraph_attribute_record_t *rec;
2453
2454
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(
2455
0
        &attr->val, name, IGRAPH_ATTRIBUTE_NUMERIC, igraph_vcount(graph), &rec
2456
0
    ));
2457
0
    VECTOR(*rec->value.as_vector)[vid] = value;
2458
2459
0
    return IGRAPH_SUCCESS;
2460
0
}
2461
2462
/**
2463
 * \function igraph_cattribute_VAB_set
2464
 * \brief Set a boolean vertex attribute.
2465
 *
2466
 * The attribute will be added if not present already. If present it
2467
 * will be overwritten. The same \p value is set for all vertices
2468
 * included in \p vid.
2469
 * \param graph The graph.
2470
 * \param name Name of the attribute.
2471
 * \param vid Vertices for which to set the attribute.
2472
 * \param value The (new) value of the attribute.
2473
 * \return Error code.
2474
 *
2475
 * \sa \ref SETVAB for a simpler way.
2476
 *
2477
 * Time complexity: O(n), the number of vertices if the attribute is
2478
 * new, O(|vid|) otherwise.
2479
 */
2480
igraph_error_t igraph_cattribute_VAB_set(igraph_t *graph, const char *name,
2481
0
                              igraph_int_t vid, igraph_bool_t value) {
2482
0
    igraph_i_cattributes_t *attr = graph->attr;
2483
0
    igraph_attribute_record_t *rec;
2484
2485
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(
2486
0
        &attr->val, name, IGRAPH_ATTRIBUTE_BOOLEAN, igraph_vcount(graph), &rec
2487
0
    ));
2488
0
    VECTOR(*rec->value.as_vector_bool)[vid] = value;
2489
2490
0
    return IGRAPH_SUCCESS;
2491
0
}
2492
2493
/**
2494
 * \function igraph_cattribute_VAS_set
2495
 * \brief Set a string vertex attribute.
2496
 *
2497
 * The attribute will be added if not present already. If present it
2498
 * will be overwritten. The same \p value is set for all vertices
2499
 * included in \p vid.
2500
 * \param graph The graph.
2501
 * \param name Name of the attribute.
2502
 * \param vid Vertices for which to set the attribute.
2503
 * \param value The (new) value of the attribute.
2504
 * \return Error code.
2505
 *
2506
 * \sa \ref SETVAS for a simpler way.
2507
 *
2508
 * Time complexity: O(n*l), n is the number of vertices, l is the
2509
 * length of the string to set. If the attribute if not new then only
2510
 * O(|vid|*l).
2511
 */
2512
igraph_error_t igraph_cattribute_VAS_set(igraph_t *graph, const char *name,
2513
0
                              igraph_int_t vid, const char *value) {
2514
0
    igraph_i_cattributes_t *attr = graph->attr;
2515
0
    igraph_attribute_record_t *rec;
2516
2517
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(
2518
0
        &attr->val, name, IGRAPH_ATTRIBUTE_STRING, igraph_vcount(graph), &rec
2519
0
    ));
2520
0
    IGRAPH_CHECK(igraph_strvector_set(rec->value.as_strvector, vid, value));
2521
2522
0
    return IGRAPH_SUCCESS;
2523
0
}
2524
2525
/**
2526
 * \function igraph_cattribute_EAN_set
2527
 * \brief Set a numeric edge attribute.
2528
 *
2529
 * The attribute will be added if not present already. If present it
2530
 * will be overwritten. The same \p value is set for all edges
2531
 * included in \p vid.
2532
 * \param graph The graph.
2533
 * \param name Name of the attribute.
2534
 * \param eid Edges for which to set the attribute.
2535
 * \param value The (new) value of the attribute.
2536
 * \return Error code.
2537
 *
2538
 * \sa \ref SETEAN for a simpler way.
2539
 *
2540
 * Time complexity: O(e), the number of edges if the attribute is
2541
 * new, O(|eid|) otherwise.
2542
 */
2543
igraph_error_t igraph_cattribute_EAN_set(igraph_t *graph, const char *name,
2544
0
                              igraph_int_t eid, igraph_real_t value) {
2545
0
    igraph_i_cattributes_t *attr = graph->attr;
2546
0
    igraph_attribute_record_t *rec;
2547
2548
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(
2549
0
        &attr->eal, name, IGRAPH_ATTRIBUTE_NUMERIC, igraph_ecount(graph), &rec
2550
0
    ));
2551
0
    VECTOR(*rec->value.as_vector)[eid] = value;
2552
2553
0
    return IGRAPH_SUCCESS;
2554
0
}
2555
2556
/**
2557
 * \function igraph_cattribute_EAB_set
2558
 * \brief Set a boolean edge attribute.
2559
 *
2560
 * The attribute will be added if not present already. If present it
2561
 * will be overwritten. The same \p value is set for all edges
2562
 * included in \p vid.
2563
 * \param graph The graph.
2564
 * \param name Name of the attribute.
2565
 * \param eid Edges for which to set the attribute.
2566
 * \param value The (new) value of the attribute.
2567
 * \return Error code.
2568
 *
2569
 * \sa \ref SETEAB for a simpler way.
2570
 *
2571
 * Time complexity: O(e), the number of edges if the attribute is
2572
 * new, O(|eid|) otherwise.
2573
 */
2574
igraph_error_t igraph_cattribute_EAB_set(igraph_t *graph, const char *name,
2575
0
                              igraph_int_t eid, igraph_bool_t value) {
2576
0
    igraph_i_cattributes_t *attr = graph->attr;
2577
0
    igraph_attribute_record_t *rec;
2578
2579
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(
2580
0
        &attr->eal, name, IGRAPH_ATTRIBUTE_BOOLEAN, igraph_ecount(graph), &rec
2581
0
    ));
2582
0
    VECTOR(*rec->value.as_vector_bool)[eid] = value;
2583
2584
0
    return IGRAPH_SUCCESS;
2585
0
}
2586
2587
/**
2588
 * \function igraph_cattribute_EAS_set
2589
 * \brief Set a string edge attribute.
2590
 *
2591
 * The attribute will be added if not present already. If present it
2592
 * will be overwritten. The same \p value is set for all edges
2593
 * included in \p vid.
2594
 * \param graph The graph.
2595
 * \param name Name of the attribute.
2596
 * \param eid Edges for which to set the attribute.
2597
 * \param value The (new) value of the attribute.
2598
 * \return Error code.
2599
 *
2600
 * \sa \ref SETEAS for a simpler way.
2601
 *
2602
 * Time complexity: O(e*l), n is the number of edges, l is the
2603
 * length of the string to set. If the attribute if not new then only
2604
 * O(|eid|*l).
2605
 */
2606
igraph_error_t igraph_cattribute_EAS_set(igraph_t *graph, const char *name,
2607
0
                              igraph_int_t eid, const char *value) {
2608
0
    igraph_i_cattributes_t *attr = graph->attr;
2609
0
    igraph_attribute_record_t *rec;
2610
2611
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(
2612
0
        &attr->eal, name, IGRAPH_ATTRIBUTE_STRING, igraph_ecount(graph), &rec
2613
0
    ));
2614
0
    IGRAPH_CHECK(igraph_strvector_set(rec->value.as_strvector, eid, value));
2615
2616
0
    return IGRAPH_SUCCESS;
2617
0
}
2618
2619
/**
2620
 * \function igraph_cattribute_VAN_setv
2621
 * \brief Set a numeric vertex attribute for all vertices.
2622
 *
2623
 * The attribute will be added if not present yet.
2624
 * \param graph The graph.
2625
 * \param name Name of the attribute.
2626
 * \param v The new attribute values. The length of this vector must
2627
 *   match the number of vertices.
2628
 * \return Error code.
2629
 *
2630
 * \sa \ref SETVANV for a simpler way.
2631
 *
2632
 * Time complexity: O(n), the number of vertices.
2633
 */
2634
2635
igraph_error_t igraph_cattribute_VAN_setv(igraph_t *graph, const char *name,
2636
0
                               const igraph_vector_t *v) {
2637
0
    igraph_i_cattributes_t *attr = graph->attr;
2638
0
    igraph_attribute_record_t *rec;
2639
0
    igraph_int_t nv = igraph_vcount(graph);
2640
2641
    /* Check length first */
2642
0
    if (igraph_vector_size(v) != nv) {
2643
0
        IGRAPH_ERROR("Invalid vertex attribute vector length.", IGRAPH_EINVAL);
2644
0
    }
2645
2646
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(&attr->val, name, IGRAPH_ATTRIBUTE_NUMERIC, nv, &rec));
2647
0
    IGRAPH_CHECK(igraph_vector_update(rec->value.as_vector, v));
2648
2649
0
    return IGRAPH_SUCCESS;
2650
0
}
2651
2652
/**
2653
 * \function igraph_cattribute_VAB_setv
2654
 * \brief Set a boolean vertex attribute for all vertices.
2655
 *
2656
 * The attribute will be added if not present yet.
2657
 * \param graph The graph.
2658
 * \param name Name of the attribute.
2659
 * \param v The new attribute values. The length of this boolean vector must
2660
 *   match the number of vertices.
2661
 * \return Error code.
2662
 *
2663
 * \sa \ref SETVANV for a simpler way.
2664
 *
2665
 * Time complexity: O(n), the number of vertices.
2666
 */
2667
2668
igraph_error_t igraph_cattribute_VAB_setv(igraph_t *graph, const char *name,
2669
0
                               const igraph_vector_bool_t *v) {
2670
0
    igraph_i_cattributes_t *attr = graph->attr;
2671
0
    igraph_attribute_record_t *rec;
2672
0
    igraph_int_t nv = igraph_vcount(graph);
2673
2674
    /* Check length first */
2675
0
    if (igraph_vector_bool_size(v) != nv) {
2676
0
        IGRAPH_ERROR("Invalid vertex attribute vector length.", IGRAPH_EINVAL);
2677
0
    }
2678
2679
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(&attr->val, name, IGRAPH_ATTRIBUTE_BOOLEAN, nv, &rec));
2680
0
    IGRAPH_CHECK(igraph_vector_bool_update(rec->value.as_vector_bool, v));
2681
2682
0
    return IGRAPH_SUCCESS;
2683
0
}
2684
2685
/**
2686
 * \function igraph_cattribute_VAS_setv
2687
 * \brief Set a string vertex attribute for all vertices.
2688
 *
2689
 * The attribute will be added if not present yet.
2690
 * \param graph The graph.
2691
 * \param name Name of the attribute.
2692
 * \param sv String vector, the new attribute values. The length of this vector must
2693
 *   match the number of vertices.
2694
 * \return Error code.
2695
 *
2696
 * \sa \ref SETVASV for a simpler way.
2697
 *
2698
 * Time complexity: O(n+l), n is the number of vertices, l is the
2699
 * total length of the strings.
2700
 */
2701
igraph_error_t igraph_cattribute_VAS_setv(igraph_t *graph, const char *name,
2702
0
                               const igraph_strvector_t *sv) {
2703
0
    igraph_i_cattributes_t *attr = graph->attr;
2704
0
    igraph_attribute_record_t *rec;
2705
0
    igraph_int_t nv = igraph_vcount(graph);
2706
2707
    /* Check length first */
2708
0
    if (igraph_strvector_size(sv) != nv) {
2709
0
        IGRAPH_ERROR("Invalid vertex attribute vector length.", IGRAPH_EINVAL);
2710
0
    }
2711
2712
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(&attr->val, name, IGRAPH_ATTRIBUTE_STRING, nv, &rec));
2713
0
    IGRAPH_CHECK(igraph_strvector_update(rec->value.as_strvector, sv));
2714
2715
0
    return IGRAPH_SUCCESS;
2716
0
}
2717
2718
/**
2719
 * \function igraph_cattribute_EAN_setv
2720
 * \brief Set a numeric edge attribute for all edges.
2721
 *
2722
 * The attribute will be added if not present yet.
2723
 * \param graph The graph.
2724
 * \param name Name of the attribute.
2725
 * \param v The new attribute values. The length of this vector must
2726
 *   match the number of edges.
2727
 * \return Error code.
2728
 *
2729
 * \sa \ref SETEANV for a simpler way.
2730
 *
2731
 * Time complexity: O(e), the number of edges.
2732
 */
2733
igraph_error_t igraph_cattribute_EAN_setv(igraph_t *graph, const char *name,
2734
0
                               const igraph_vector_t *v) {
2735
0
    igraph_i_cattributes_t *attr = graph->attr;
2736
0
    igraph_attribute_record_t *rec;
2737
0
    igraph_int_t ne = igraph_ecount(graph);
2738
2739
    /* Check length first */
2740
0
    if (igraph_vector_size(v) != ne) {
2741
0
        IGRAPH_ERROR("Invalid edge attribute vector length.", IGRAPH_EINVAL);
2742
0
    }
2743
2744
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(&attr->eal, name, IGRAPH_ATTRIBUTE_NUMERIC, ne, &rec));
2745
0
    IGRAPH_CHECK(igraph_vector_update(rec->value.as_vector, v));
2746
2747
0
    return IGRAPH_SUCCESS;
2748
0
}
2749
2750
/**
2751
 * \function igraph_cattribute_EAB_setv
2752
 * \brief Set a boolean edge attribute for all edges.
2753
 *
2754
 * The attribute will be added if not present yet.
2755
 * \param graph The graph.
2756
 * \param name Name of the attribute.
2757
 * \param v The new attribute values. The length of this vector must
2758
 *   match the number of edges.
2759
 * \return Error code.
2760
 *
2761
 * \sa \ref SETEABV for a simpler way.
2762
 *
2763
 * Time complexity: O(e), the number of edges.
2764
 */
2765
igraph_error_t igraph_cattribute_EAB_setv(igraph_t *graph, const char *name,
2766
0
                               const igraph_vector_bool_t *v) {
2767
0
    igraph_i_cattributes_t *attr = graph->attr;
2768
0
    igraph_attribute_record_t *rec;
2769
0
    igraph_int_t ne = igraph_ecount(graph);
2770
2771
    /* Check length first */
2772
0
    if (igraph_vector_bool_size(v) != ne) {
2773
0
        IGRAPH_ERROR("Invalid edge attribute vector length.", IGRAPH_EINVAL);
2774
0
    }
2775
2776
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(&attr->eal, name, IGRAPH_ATTRIBUTE_BOOLEAN, ne, &rec));
2777
0
    IGRAPH_CHECK(igraph_vector_bool_update(rec->value.as_vector_bool, v));
2778
2779
0
    return IGRAPH_SUCCESS;
2780
0
}
2781
2782
/**
2783
 * \function igraph_cattribute_EAS_setv
2784
 * \brief Set a string edge attribute for all edges.
2785
 *
2786
 * The attribute will be added if not present yet.
2787
 * \param graph The graph.
2788
 * \param name Name of the attribute.
2789
 * \param sv String vector, the new attribute values. The length of this vector must
2790
 *   match the number of edges.
2791
 * \return Error code.
2792
 *
2793
 * \sa \ref SETEASV for a simpler way.
2794
 *
2795
 * Time complexity: O(e+l), e is the number of edges, l is the
2796
 * total length of the strings.
2797
 */
2798
igraph_error_t igraph_cattribute_EAS_setv(igraph_t *graph, const char *name,
2799
0
                               const igraph_strvector_t *sv) {
2800
0
    igraph_i_cattributes_t *attr = graph->attr;
2801
0
    igraph_attribute_record_t *rec;
2802
0
    igraph_int_t ne = igraph_ecount(graph);
2803
2804
    /* Check length first */
2805
0
    if (igraph_strvector_size(sv) != ne) {
2806
0
        IGRAPH_ERROR("Invalid edge attribute vector length.", IGRAPH_EINVAL);
2807
0
    }
2808
2809
0
    IGRAPH_CHECK(igraph_i_cattribute_find_or_create(&attr->eal, name, IGRAPH_ATTRIBUTE_STRING, ne, &rec));
2810
0
    IGRAPH_CHECK(igraph_strvector_update(rec->value.as_strvector, sv));
2811
2812
0
    return IGRAPH_SUCCESS;
2813
0
}
2814
2815
/**
2816
 * \function igraph_cattribute_remove_g
2817
 * \brief Remove a graph attribute.
2818
 *
2819
 * \param graph The graph object.
2820
 * \param name Name of the graph attribute to remove.
2821
 *
2822
 * \sa \ref DELGA for a simpler way.
2823
 *
2824
 */
2825
0
void igraph_cattribute_remove_g(igraph_t *graph, const char *name) {
2826
2827
0
    igraph_i_cattributes_t *attr = graph->attr;
2828
0
    igraph_attribute_record_list_t *gal = &attr->gal;
2829
0
    igraph_int_t j = igraph_i_cattribute_find_index(gal, name);
2830
2831
0
    if (j >= 0) {
2832
0
        igraph_attribute_record_list_discard(gal, j);
2833
0
    } else {
2834
0
        IGRAPH_WARNING("Cannot remove non-existent graph attribute");
2835
0
    }
2836
0
}
2837
2838
/**
2839
 * \function igraph_cattribute_remove_v
2840
 * \brief Remove a vertex attribute.
2841
 *
2842
 * \param graph The graph object.
2843
 * \param name Name of the vertex attribute to remove.
2844
 *
2845
 * \sa \ref DELVA for a simpler way.
2846
 *
2847
 */
2848
0
void igraph_cattribute_remove_v(igraph_t *graph, const char *name) {
2849
2850
0
    igraph_i_cattributes_t *attr = graph->attr;
2851
0
    igraph_attribute_record_list_t *val = &attr->val;
2852
0
    igraph_int_t j = igraph_i_cattribute_find_index(val, name);
2853
2854
0
    if (j >= 0) {
2855
0
        igraph_attribute_record_list_discard(val, j);
2856
0
    } else {
2857
0
        IGRAPH_WARNING("Cannot remove non-existent graph attribute");
2858
0
    }
2859
0
}
2860
2861
/**
2862
 * \function igraph_cattribute_remove_e
2863
 * \brief Remove an edge attribute.
2864
 *
2865
 * \param graph The graph object.
2866
 * \param name Name of the edge attribute to remove.
2867
 *
2868
 * \sa \ref DELEA for a simpler way.
2869
 */
2870
0
void igraph_cattribute_remove_e(igraph_t *graph, const char *name) {
2871
2872
0
    igraph_i_cattributes_t *attr = graph->attr;
2873
0
    igraph_attribute_record_list_t *eal = &attr->eal;
2874
0
    igraph_int_t j = igraph_i_cattribute_find_index(eal, name);
2875
2876
0
    if (j >= 0) {
2877
0
        igraph_attribute_record_list_discard(eal, j);
2878
0
    } else {
2879
0
        IGRAPH_WARNING("Cannot remove non-existent graph attribute");
2880
0
    }
2881
0
}
2882
2883
/**
2884
 * \function igraph_cattribute_remove_all
2885
 * \brief Remove all graph/vertex/edge attributes.
2886
 *
2887
 * \param graph The graph object.
2888
 * \param g Boolean, whether to remove graph attributes.
2889
 * \param v Boolean, whether to remove vertex attributes.
2890
 * \param e Boolean, whether to remove edge attributes.
2891
 *
2892
 * \sa \ref DELGAS, \ref DELVAS, \ref DELEAS, \ref DELALL for simpler
2893
 * ways.
2894
 */
2895
void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g,
2896
0
                                  igraph_bool_t v, igraph_bool_t e) {
2897
2898
0
    igraph_i_cattributes_t *attr = graph->attr;
2899
2900
0
    if (g) {
2901
0
        igraph_attribute_record_list_clear(&attr->gal);
2902
0
    }
2903
0
    if (v) {
2904
0
        igraph_attribute_record_list_clear(&attr->val);
2905
0
    }
2906
0
    if (e) {
2907
0
        igraph_attribute_record_list_clear(&attr->eal);
2908
0
    }
2909
0
}