Coverage Report

Created: 2025-06-20 06:27

/src/igraph/src/graph/attributes.c
Line
Count
Source (jump to first uncovered line)
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
   This program is distributed in the hope that it will be useful,
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
   GNU General Public License for more details.
15
16
   You should have received a copy of the GNU General Public License
17
   along with this program; if not, write to the Free Software
18
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
   02110-1301 USA
20
21
*/
22
23
#include "igraph_attributes.h"
24
#include "igraph_memory.h"
25
26
#include "graph/attributes.h"
27
#include "internal/hacks.h" /* strdup */
28
29
#include <string.h>
30
#include <stdarg.h>
31
32
/**
33
 * \section about_attributes
34
 *
35
 * <para>Attributes are numbers, boolean values or strings associated with
36
 * the vertices or edges of a graph, or with the graph itself. E.g. you may
37
 * label vertices with symbolic names or attach numeric weights to the edges
38
 * of a graph. In addition to these three basic types, a custom object
39
 * type is supported as well.</para>
40
 *
41
 * <para>igraph attributes are designed to be flexible and extensible.
42
 * In igraph attributes are implemented via an interface abstraction:
43
 * any type implementing the functions in the interface can be used
44
 * for storing vertex, edge and graph attributes. This means that
45
 * different attribute implementations can be used together with
46
 * igraph. This is reasonable: if igraph is used from Python attributes can be
47
 * of any Python type, from R all R types are allowed. There is also an
48
 * experimental attribute implementation to be used when programming
49
 * in C, but by default it is currently turned off.</para>
50
 *
51
 * <para>First we briefly look over how attribute handlers can be
52
 * implemented. This is not something a user does every day. It is
53
 * rather typically the job of the high level interface writers. (But
54
 * it is possible to write an interface without implementing
55
 * attributes.) Then we show the experimental C attribute handler.</para>
56
 */
57
58
/**
59
 * \section about_attribute_table
60
 * <para>It is possible to attach an attribute handling
61
 * interface to \a igraph. This is simply a table of functions, of
62
 * type \ref igraph_attribute_table_t. These functions are invoked to
63
 * notify the attribute handling code about the structural changes in
64
 * a graph. See the documentation of this type for details.</para>
65
 *
66
 * <para>By default there is no attribute interface attached to \a igraph.
67
 * To attach one, call \ref igraph_set_attribute_table with your new
68
 * table. This is normally done on program startup, and is kept untouched
69
 * for the program's lifetime. It must be done before any graph object
70
 * is created, as graphs created with a given attribute handler
71
 * cannot be manipulated while a different attribute handler is
72
 * active.</para>
73
 */
74
75
/**
76
 * \section about_attribute_record
77
 *
78
 * <para>Functions in the attribute handler interface may refer to
79
 * \em "attribute records" or \em "attribute record lists". An attribute record
80
 * is simply a triplet consisting of an attribute name, an attribute type and
81
 * a vector containing the values of the attribute. Attribute record lists are
82
 * typed containers that contain a sequence of attribute records. Attribute
83
 * record lists own the attribute records that they contain, and similarly,
84
 * attribute records own the vectors contained in them. Destroying an attribute
85
 * record destroys the vector of values inside it, and destroying an attribute
86
 * record list destroys all attribute records in the list.</para>
87
 */
88
89
/**
90
 * \section about_attribute_combination
91
 *
92
 * <para>Several graph operations may collapse multiple vertices or edges into
93
 * a single one. Attribute combination lists are used to indicate to the attribute
94
 * handler how to combine the attributes of the original vertices or edges and
95
 * how to derive the final attribute value that is to be assigned to the collapsed
96
 * vertex or edge. For example, \ref igraph_simplify() removes loops and combines
97
 * multiple edges into a single one; in case of a graph with an edge attribute
98
 * named \c weight the attribute combination list can tell the attribute handler
99
 * whether the weight of a collapsed edge should be the sum, the mean or some other
100
 * function of the weights of the original edges that were collapsed into one.</para>
101
 *
102
 * <para>One attribute combination list may contain several attribute combination
103
 * records, one for each vertex or edge attribute that is to be handled during the
104
 * operation.</para>
105
 */
106
107
static void igraph_i_attribute_record_set_type(
108
    igraph_attribute_record_t *attr, igraph_attribute_type_t type, void *ptr
109
);
110
static void igraph_i_attribute_record_destroy_values(igraph_attribute_record_t *attr);
111
112
/**
113
 * \function igraph_attribute_record_init
114
 * \brief Initializes an attribute record with a given name and type.
115
 *
116
 * \param attr  the attribute record to initialize
117
 * \param name  name of the attribute
118
 * \param type  type of the attribute
119
 * \return Error code:
120
 *         \c IGRAPH_ENOMEM if there is not enough memory.
121
 *
122
 * Time complexity: O(1).
123
 */
124
igraph_error_t igraph_attribute_record_init(
125
    igraph_attribute_record_t *attr, const char* name, igraph_attribute_type_t type
126
15.5k
) {
127
15.5k
    attr->name = NULL;
128
15.5k
    attr->type = IGRAPH_ATTRIBUTE_UNSPECIFIED;
129
15.5k
    attr->value.as_raw = NULL;
130
15.5k
    attr->default_value.string = NULL;
131
132
15.5k
    IGRAPH_CHECK(igraph_attribute_record_set_name(attr, name));
133
15.5k
    IGRAPH_CHECK(igraph_attribute_record_set_type(attr, type));
134
135
15.5k
    return IGRAPH_SUCCESS;
136
15.5k
}
137
138
/**
139
 * \function igraph_attribute_record_init_copy
140
 * \brief Initializes an attribute record by copying another record.
141
 *
142
 * </para><para>
143
 * Copies made by this function are deep copies: a full copy of the value
144
 * vector contained in the record is placed in the new record so they become
145
 * independent of each other.
146
 *
147
 * \param to    the attribute record to initialize
148
 * \param from  the attribute record to copy data from
149
 * \return Error code:
150
 *         \c IGRAPH_ENOMEM if there is not enough memory.
151
 *
152
 * Time complexity: operating system dependent, usually O(n), where n is the
153
 * size of the value vector in the attribute record.
154
 */
155
igraph_error_t igraph_attribute_record_init_copy(
156
    igraph_attribute_record_t *to, const igraph_attribute_record_t *from
157
0
) {
158
0
    IGRAPH_CHECK(igraph_attribute_record_init(to, from->name, from->type));
159
160
0
    switch (from->type) {
161
0
        case IGRAPH_ATTRIBUTE_NUMERIC:
162
0
            IGRAPH_CHECK(igraph_vector_update(to->value.as_vector, from->value.as_vector));
163
0
            break;
164
165
0
        case IGRAPH_ATTRIBUTE_STRING:
166
0
            IGRAPH_CHECK(igraph_strvector_update(to->value.as_strvector, from->value.as_strvector));
167
0
            break;
168
169
0
        case IGRAPH_ATTRIBUTE_BOOLEAN:
170
0
            IGRAPH_CHECK(igraph_vector_bool_update(to->value.as_vector_bool, from->value.as_vector_bool));
171
0
            break;
172
173
0
        case IGRAPH_ATTRIBUTE_UNSPECIFIED:
174
0
            break;
175
176
0
        default:
177
0
            break;
178
0
    }
179
180
0
    return IGRAPH_SUCCESS;
181
0
}
182
183
31.0k
static void igraph_i_attribute_record_destroy_values(igraph_attribute_record_t *attr) {
184
31.0k
    IGRAPH_ASSERT(attr != NULL);
185
186
31.0k
    if (attr->value.as_raw) {
187
15.5k
        switch (attr->type) {
188
4.00k
            case IGRAPH_ATTRIBUTE_NUMERIC:
189
4.00k
                igraph_vector_destroy(attr->value.as_vector);
190
4.00k
                break;
191
192
10.7k
            case IGRAPH_ATTRIBUTE_STRING:
193
10.7k
                igraph_strvector_destroy(attr->value.as_strvector);
194
10.7k
                break;
195
196
752
            case IGRAPH_ATTRIBUTE_BOOLEAN:
197
752
                igraph_vector_bool_destroy(attr->value.as_vector_bool);
198
752
                break;
199
200
0
            default:
201
0
                break;
202
15.5k
        }
203
204
15.5k
        igraph_free(attr->value.as_raw);
205
15.5k
        attr->value.as_raw = NULL;
206
15.5k
    }
207
208
31.0k
    switch (attr->type) {
209
4.00k
        case IGRAPH_ATTRIBUTE_NUMERIC:
210
4.00k
            attr->default_value.numeric = 0;
211
4.00k
            break;
212
213
10.7k
        case IGRAPH_ATTRIBUTE_STRING:
214
10.7k
            if (attr->default_value.string) {
215
300
                igraph_free(attr->default_value.string);
216
300
                attr->default_value.string = NULL;
217
300
            }
218
10.7k
            break;
219
220
752
        case IGRAPH_ATTRIBUTE_BOOLEAN:
221
752
            attr->default_value.boolean = 0;
222
752
            break;
223
224
15.5k
        default:
225
15.5k
            break;
226
31.0k
    }
227
228
31.0k
    attr->type = IGRAPH_ATTRIBUTE_UNSPECIFIED;
229
31.0k
}
230
231
/**
232
 * \function igraph_attribute_record_destroy
233
 * \brief Destroys an attribute record.
234
 *
235
 * \param attr  the previously initialized attribute record to destroy.
236
 *
237
 * Time complexity: operating system dependent.
238
 */
239
15.5k
void igraph_attribute_record_destroy(igraph_attribute_record_t *attr) {
240
15.5k
    igraph_i_attribute_record_destroy_values(attr);
241
242
15.5k
    if (attr->name) {
243
15.5k
        igraph_free(attr->name);
244
15.5k
        attr->name = NULL;
245
15.5k
    }
246
15.5k
}
247
248
/**
249
 * \function igraph_attribute_record_check_type
250
 * \brief Checks whether the type of the attribute record is equal to an expected type.
251
 *
252
 * \param attr  the attribute record to test
253
 * \param type  the expected type of the attribute record
254
 * \return Error code:
255
 *         \c IGRAPH_EINVAL if the type of the attribute record is not equal to
256
 *         the expected type
257
 *
258
 * Time complexity: O(1).
259
 */
260
igraph_error_t igraph_attribute_record_check_type(
261
    const igraph_attribute_record_t *attr, igraph_attribute_type_t type
262
34.4M
) {
263
34.4M
    if (type != attr->type) {
264
0
        switch (type) {
265
0
            case IGRAPH_ATTRIBUTE_STRING:
266
0
                IGRAPH_ERROR("String attribute expected.", IGRAPH_EINVAL);
267
0
                break;
268
0
            case IGRAPH_ATTRIBUTE_NUMERIC:
269
0
                IGRAPH_ERROR("Numeric attribute expected.", IGRAPH_EINVAL);
270
0
                break;
271
0
            case IGRAPH_ATTRIBUTE_BOOLEAN:
272
0
                IGRAPH_ERROR("Boolean attribute expected.", IGRAPH_EINVAL);
273
0
                break;
274
0
            case IGRAPH_ATTRIBUTE_OBJECT:
275
0
                IGRAPH_ERROR("Object attribute expected.", IGRAPH_EINVAL);
276
0
                break;
277
0
            default:
278
0
                IGRAPH_ERROR("Attribute with unknown type expected.", IGRAPH_EINVAL);
279
0
                break;
280
0
        }
281
0
    }
282
283
34.4M
    return IGRAPH_SUCCESS;
284
34.4M
}
285
286
/**
287
 * \function igraph_attribute_record_size
288
 * \brief Returns the size of the value vector in an attribute record.
289
 *
290
 * \param attr      the attribute record to query
291
 * \return the number of elements in the value vector of the attribute record
292
 */
293
14.0k
igraph_integer_t igraph_attribute_record_size(const igraph_attribute_record_t *attr) {
294
14.0k
    IGRAPH_ASSERT(attr != NULL);
295
296
14.0k
    switch (attr->type) {
297
3.58k
        case IGRAPH_ATTRIBUTE_NUMERIC:
298
3.58k
            return igraph_vector_size(attr->value.as_vector);
299
300
9.68k
        case IGRAPH_ATTRIBUTE_STRING:
301
9.68k
            return igraph_strvector_size(attr->value.as_strvector);
302
303
740
        case IGRAPH_ATTRIBUTE_BOOLEAN:
304
740
            return igraph_vector_bool_size(attr->value.as_vector_bool);
305
306
0
        case IGRAPH_ATTRIBUTE_UNSPECIFIED:
307
0
            return 0;
308
309
0
        default:
310
0
            IGRAPH_ERRORF("Unsupported attribute type: %d", IGRAPH_EINVAL, (int) attr->type);
311
14.0k
    }
312
14.0k
}
313
314
/**
315
 * \function igraph_attribute_record_resize
316
 * \brief Resizes the value vector in an attribute record.
317
 *
318
 * </para><para>When the value vector is shorter than the desired length, it
319
 * will be expanded with \c IGRAPH_NAN for numeric vectors, \c false for Boolean
320
 * vectors and empty strings for string vectors.
321
 *
322
 * \param attr      the attribute record to update
323
 * \param new_size  the new size of the value vector
324
 * \return Error code:
325
 *         \c IGRAPH_ENOMEM if there is not enough memory.
326
 *         \c IGRAPH_EINVAL if the type of the attribute record is not specified yet.
327
 */
328
igraph_error_t igraph_attribute_record_resize(
329
    igraph_attribute_record_t *attr, igraph_integer_t new_size
330
17.7k
) {
331
17.7k
    igraph_integer_t i;
332
17.7k
    igraph_vector_t *vec;
333
17.7k
    igraph_vector_bool_t *log;
334
17.7k
    igraph_strvector_t *str;
335
336
17.7k
    IGRAPH_ASSERT(attr != NULL);
337
338
17.7k
    switch (attr->type) {
339
340
9.54k
        case IGRAPH_ATTRIBUTE_NUMERIC:
341
9.54k
            vec = attr->value.as_vector;
342
9.54k
            i = igraph_vector_size(vec);
343
9.54k
            IGRAPH_CHECK(igraph_vector_resize(vec, new_size));
344
8.75M
            while (i < new_size) {
345
8.74M
                VECTOR(*vec)[i++] = attr->default_value.numeric;
346
8.74M
            }
347
9.54k
            break;
348
349
382
        case IGRAPH_ATTRIBUTE_BOOLEAN:
350
382
            log = attr->value.as_vector_bool;
351
382
            i = igraph_vector_bool_size(log);
352
382
            IGRAPH_CHECK(igraph_vector_bool_resize(log, new_size));
353
11.4M
            while (i < new_size) {
354
11.4M
                VECTOR(*log)[i++] = attr->default_value.boolean;
355
11.4M
            }
356
382
            break;
357
358
7.81k
        case IGRAPH_ATTRIBUTE_STRING:
359
7.81k
            str = attr->value.as_strvector;
360
7.81k
            if (attr->default_value.string == 0 || (*attr->default_value.string == 0)) {
361
7.40k
                IGRAPH_CHECK(igraph_strvector_resize(str, new_size));
362
7.40k
            } else {
363
408
                i = igraph_strvector_size(str);
364
408
                IGRAPH_CHECK(igraph_strvector_resize(str, new_size));
365
2.51M
                while (i < new_size) {
366
2.51M
                    IGRAPH_CHECK(igraph_strvector_set(str, i++, attr->default_value.string));
367
2.51M
                }
368
408
            }
369
7.81k
            break;
370
371
7.81k
        case IGRAPH_ATTRIBUTE_UNSPECIFIED:
372
0
            IGRAPH_ERROR("Attribute record has no type yet.", IGRAPH_EINVAL);
373
0
            break;
374
375
0
        default:
376
0
            IGRAPH_ERRORF("Unsupported attribute type: %d", IGRAPH_EINVAL, (int) attr->type);
377
17.7k
    }
378
379
17.7k
    return IGRAPH_SUCCESS;
380
17.7k
}
381
382
/**
383
 * \function igraph_attribute_record_set_default_numeric
384
 * \brief Sets the default value of the attribute to the given number.
385
 *
386
 * </para><para>
387
 * This function must be called for numeric attribute records only. When not
388
 * specified, the default value of numeric attributes is NaN.
389
 *
390
 * \param attr   the attribute record to update
391
 * \param value  the new default value
392
 * \return Error code:
393
 *         \c IGRAPH_EINVAL if the attribute record has a non-numeric type
394
 */
395
igraph_error_t igraph_attribute_record_set_default_numeric(
396
    igraph_attribute_record_t *attr, igraph_real_t value
397
6.17k
) {
398
6.17k
    if (attr->type != IGRAPH_ATTRIBUTE_NUMERIC) {
399
0
        return IGRAPH_EINVAL;
400
0
    }
401
402
6.17k
    attr->default_value.numeric = value;
403
6.17k
    return IGRAPH_SUCCESS;
404
6.17k
}
405
406
/**
407
 * \function igraph_attribute_record_set_default_boolean
408
 * \brief Sets the default value of the attribute to the given logical value.
409
 *
410
 * </para><para>
411
 * This function must be called for Boolean attribute records only. When not
412
 * specified, the default value of Boolean attributes is \c false.
413
 *
414
 * \param attr   the attribute record to update
415
 * \param value  the new default value
416
 * \return Error code:
417
 *         \c IGRAPH_EINVAL if the attribute record is not of Boolean type
418
 */
419
IGRAPH_EXPORT igraph_error_t igraph_attribute_record_set_default_boolean(
420
    igraph_attribute_record_t *attr, igraph_bool_t value
421
0
) {
422
0
    if (attr->type != IGRAPH_ATTRIBUTE_BOOLEAN) {
423
0
        return IGRAPH_EINVAL;
424
0
    }
425
426
0
    attr->default_value.boolean = value;
427
0
    return IGRAPH_SUCCESS;
428
0
}
429
430
/**
431
 * \function igraph_attribute_record_set_default_string
432
 * \brief Sets the default value of the attribute to the given string.
433
 *
434
 * </para><para>
435
 * This function must be called for string attribute records only. When not
436
 * specified, the default value of string attributes is an empty string.
437
 *
438
 * \param attr   the attribute record to update
439
 * \param value  the new default value. \c NULL means an empty string.
440
 *
441
 * \return Error code:
442
 *         \c IGRAPH_ENOMEM if there is not enough memory
443
 *         \c IGRAPH_EINVAL if the attribute record is not of string type
444
 */
445
IGRAPH_EXPORT igraph_error_t igraph_attribute_record_set_default_string(
446
    igraph_attribute_record_t *attr, const char* value
447
5.79k
) {
448
5.79k
    char* copy;
449
450
5.79k
    if (attr->type != IGRAPH_ATTRIBUTE_STRING) {
451
0
        return IGRAPH_EINVAL;
452
0
    }
453
454
5.79k
    if (value && (*value != 0)) {
455
300
        copy = strdup(value);
456
300
        IGRAPH_CHECK_OOM(copy, "Insufficient memory to duplicate default value.");
457
5.49k
    } else {
458
5.49k
        copy = NULL;
459
5.49k
    }
460
461
5.79k
    if (attr->default_value.string) {
462
0
        igraph_free(attr->default_value.string);
463
0
    }
464
5.79k
    attr->default_value.string = copy;
465
466
5.79k
    return IGRAPH_SUCCESS;
467
5.79k
}
468
469
470
/**
471
 * \function igraph_attribute_record_set_name
472
 * \brief Sets the attribute name in an attribute record.
473
 *
474
 * \param attr  the attribute record to update
475
 * \param name  the new name
476
 * \return Error code:
477
 *         \c IGRAPH_ENOMEM if there is not enough memory.
478
 */
479
igraph_error_t igraph_attribute_record_set_name(
480
    igraph_attribute_record_t *attr, const char* name
481
23.0k
) {
482
23.0k
    char* new_name;
483
484
23.0k
    IGRAPH_ASSERT(attr != NULL);
485
486
23.0k
    if (name != NULL) {
487
15.5k
        new_name = strdup(name);
488
15.5k
        IGRAPH_CHECK_OOM(new_name, "Insufficient memory for allocating attribute name.");
489
15.5k
    } else {
490
7.55k
        new_name = NULL;
491
7.55k
    }
492
493
23.0k
    if (attr->name) {
494
0
        igraph_free(attr->name);
495
0
    }
496
497
23.0k
    attr->name = new_name;
498
499
23.0k
    return IGRAPH_SUCCESS;
500
23.0k
}
501
502
static void igraph_i_attribute_record_set_type(
503
    igraph_attribute_record_t *attr, igraph_attribute_type_t type, void *ptr
504
15.5k
) {
505
15.5k
    bool type_changed = attr->type != type;
506
507
15.5k
    if (type_changed || attr->value.as_raw != ptr) {
508
15.5k
        igraph_i_attribute_record_destroy_values(attr);
509
15.5k
        attr->type = type;
510
15.5k
        attr->value.as_raw = ptr;
511
15.5k
    }
512
513
15.5k
    if (type_changed && type == IGRAPH_ATTRIBUTE_NUMERIC) {
514
4.00k
        IGRAPH_ASSERT(
515
4.00k
            igraph_attribute_record_set_default_numeric(attr, IGRAPH_NAN) == IGRAPH_SUCCESS
516
4.00k
        );
517
4.00k
    }
518
15.5k
}
519
520
/**
521
 * \function igraph_attribute_record_set_type
522
 * \brief Sets the type of an attribute record.
523
 *
524
 * </para><para>
525
 * When the new type being set is different from the old type, any values already
526
 * stored in the attribute record will be destroyed and a new, empty attribute
527
 * value vector will be allocated. When the new type is the same as the old
528
 * type, this function is a no-op.
529
 *
530
 * \param attr  the attribute record to update
531
 * \param type  the new type
532
 * \return Error code:
533
 *         \c IGRAPH_ENOMEM if there is not enough memory.
534
 */
535
igraph_error_t igraph_attribute_record_set_type(
536
    igraph_attribute_record_t *attr, igraph_attribute_type_t type
537
23.0k
) {
538
23.0k
    void* ptr;
539
540
23.0k
    IGRAPH_ASSERT(attr != NULL);
541
542
23.0k
    if (attr->type != type) {
543
15.5k
        switch (type) {
544
4.00k
            case IGRAPH_ATTRIBUTE_NUMERIC: {
545
4.00k
                igraph_vector_t* vec = IGRAPH_CALLOC(1, igraph_vector_t);
546
4.00k
                IGRAPH_CHECK_OOM(vec, "Insufficient memory for attribute record.");
547
4.00k
                IGRAPH_FINALLY(igraph_free, vec);
548
4.00k
                IGRAPH_VECTOR_INIT_FINALLY(vec, 0);
549
4.00k
                ptr = vec;
550
4.00k
            }
551
0
            break;
552
553
10.7k
            case IGRAPH_ATTRIBUTE_STRING: {
554
10.7k
                igraph_strvector_t* strvec = IGRAPH_CALLOC(1, igraph_strvector_t);
555
10.7k
                IGRAPH_CHECK_OOM(strvec, "Insufficient memory for attribute record.");
556
10.7k
                IGRAPH_FINALLY(igraph_free, strvec);
557
10.7k
                IGRAPH_CHECK(igraph_strvector_init(strvec, 0));
558
10.7k
                IGRAPH_FINALLY(igraph_strvector_destroy, strvec);
559
10.7k
                ptr = strvec;
560
10.7k
            }
561
0
            break;
562
563
752
            case IGRAPH_ATTRIBUTE_BOOLEAN: {
564
752
                igraph_vector_bool_t* boolvec = IGRAPH_CALLOC(1, igraph_vector_bool_t);
565
752
                IGRAPH_CHECK_OOM(boolvec, "Insufficient memory for attribute record.");
566
752
                IGRAPH_FINALLY(igraph_free, boolvec);
567
752
                IGRAPH_VECTOR_BOOL_INIT_FINALLY(boolvec, 0);
568
752
                ptr = boolvec;
569
752
            }
570
0
            break;
571
572
0
            default:
573
0
                IGRAPH_ERRORF("Unsupported attribute type: %d", IGRAPH_EINVAL, (int) type);
574
15.5k
        }
575
576
15.5k
        igraph_i_attribute_record_set_type(attr, type, ptr);
577
15.5k
        IGRAPH_FINALLY_CLEAN(2);
578
15.5k
    }
579
580
23.0k
    return IGRAPH_SUCCESS;
581
23.0k
}
582
583
#define ATTRIBUTE_RECORD_LIST
584
#define BASE_ATTRIBUTE_RECORD
585
#define CUSTOM_INIT_DESTROY
586
#include "igraph_pmt.h"
587
#include "../core/typed_list.pmt"
588
#include "igraph_pmt_off.h"
589
#undef CUSTOM_INIT_DESTROY
590
#undef BASE_ATTRIBUTE_RECORD
591
#undef ATTRIBUTE_RECORD_LIST
592
593
static igraph_error_t igraph_i_attribute_record_list_init_item(
594
    const igraph_attribute_record_list_t* list, igraph_attribute_record_t* item
595
7.55k
) {
596
7.55k
    IGRAPH_UNUSED(list);
597
7.55k
    return igraph_attribute_record_init(item, NULL, IGRAPH_ATTRIBUTE_UNSPECIFIED);
598
7.55k
}
599
600
static igraph_error_t igraph_i_attribute_record_list_copy_item(
601
    igraph_attribute_record_t* dest, const igraph_attribute_record_t* source
602
0
) {
603
0
    return igraph_attribute_record_init_copy(dest, source);
604
0
}
605
606
15.5k
static void igraph_i_attribute_record_list_destroy_item(igraph_attribute_record_t* item) {
607
15.5k
    igraph_attribute_record_destroy(item);
608
15.5k
}
609
610
/* Should you ever want to have a thread-local attribute handler table, prepend
611
 * IGRAPH_THREAD_LOCAL to the following declaration and #include "config.h". */
612
igraph_attribute_table_t *igraph_i_attribute_table = NULL;
613
614
igraph_error_t igraph_i_attribute_init(
615
    igraph_t *graph, const igraph_attribute_record_list_t *attr
616
2.42k
) {
617
2.42k
    graph->attr = NULL;
618
2.42k
    if (igraph_i_attribute_table) {
619
2.42k
        IGRAPH_CHECK(igraph_i_attribute_table->init(graph, attr));
620
2.42k
        if (graph->attr == NULL) {
621
0
            IGRAPH_ERROR("Attribute handler did not initialize attr pointer", IGRAPH_FAILURE);
622
0
        }
623
2.42k
    }
624
2.42k
    return IGRAPH_SUCCESS;
625
2.42k
}
626
627
2.42k
void igraph_i_attribute_destroy(igraph_t *graph) {
628
2.42k
    if (graph->attr && igraph_i_attribute_table) {
629
2.42k
        igraph_i_attribute_table->destroy(graph);
630
2.42k
    }
631
2.42k
    graph->attr = NULL;
632
2.42k
}
633
634
igraph_error_t igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga,
635
0
                            igraph_bool_t va, igraph_bool_t ea) {
636
0
    igraph_i_attribute_destroy(to);
637
0
    if (from->attr && igraph_i_attribute_table) {
638
0
        IGRAPH_CHECK(igraph_i_attribute_table->copy(to, from, ga, va, ea));
639
0
        if (to->attr == NULL) {
640
0
            IGRAPH_ERROR("Attribute handler did not initialize attr pointer", IGRAPH_FAILURE);
641
0
        }
642
0
    }
643
0
    return IGRAPH_SUCCESS;
644
0
}
645
646
igraph_error_t igraph_i_attribute_add_vertices(
647
    igraph_t *graph, igraph_integer_t nv, const igraph_attribute_record_list_t *attr
648
4.84k
) {
649
4.84k
    if (igraph_i_attribute_table) {
650
4.84k
        return igraph_i_attribute_table->add_vertices(graph, nv, attr);
651
4.84k
    } else {
652
0
        return IGRAPH_SUCCESS;
653
0
    }
654
4.84k
}
655
656
igraph_error_t igraph_i_attribute_permute_vertices(const igraph_t *graph,
657
                                        igraph_t *newgraph,
658
0
                                        const igraph_vector_int_t *idx) {
659
    /* graph and newgraph may be the same, in which case we need to support
660
     * in-place operations. If they are _not_ the same, it is assumed that the
661
     * new graph has no vertex attributes yet */
662
0
    if (igraph_i_attribute_table) {
663
0
        return igraph_i_attribute_table->permute_vertices(graph, newgraph, idx);
664
0
    } else {
665
0
        return IGRAPH_SUCCESS;
666
0
    }
667
0
}
668
669
igraph_error_t igraph_i_attribute_combine_vertices(const igraph_t *graph,
670
                                        igraph_t *newgraph,
671
                                        const igraph_vector_int_list_t *merges,
672
0
                                        const igraph_attribute_combination_t *comb) {
673
    /* It is assumed that the two graphs are not the same and that the new
674
     * graph has no vertex attributes yet. We cannot assert the latter but we
675
     * can assert the former */
676
0
    IGRAPH_ASSERT(graph != newgraph);
677
0
    if (igraph_i_attribute_table) {
678
0
        return igraph_i_attribute_table->combine_vertices(graph, newgraph,
679
0
                merges,
680
0
                comb);
681
0
    } else {
682
0
        return IGRAPH_SUCCESS;
683
0
    }
684
0
}
685
686
igraph_error_t igraph_i_attribute_add_edges(
687
    igraph_t *graph, const igraph_vector_int_t *edges,
688
    const igraph_attribute_record_list_t *attr
689
2.33k
) {
690
2.33k
    if (igraph_i_attribute_table) {
691
2.33k
        return igraph_i_attribute_table->add_edges(graph, edges, attr);
692
2.33k
    } else {
693
0
        return IGRAPH_SUCCESS;
694
0
    }
695
2.33k
}
696
697
igraph_error_t igraph_i_attribute_permute_edges(const igraph_t *graph,
698
                                     igraph_t *newgraph,
699
0
                                     const igraph_vector_int_t *idx) {
700
    /* graph and newgraph may be the same, in which case we need to support
701
     * in-place operations. If they are _not_ the same, it is assumed that the
702
     * new graph has no edge attributes yet */
703
0
    if (igraph_i_attribute_table) {
704
0
        return igraph_i_attribute_table->permute_edges(graph, newgraph, idx);
705
0
    } else {
706
0
        return IGRAPH_SUCCESS;
707
0
    }
708
0
}
709
710
igraph_error_t igraph_i_attribute_combine_edges(const igraph_t *graph,
711
                                     igraph_t *newgraph,
712
                                     const igraph_vector_int_list_t *merges,
713
0
                                     const igraph_attribute_combination_t *comb) {
714
    /* It is assumed that the two graphs are not the same and that the new
715
     * graph has no eedge attributes yet. We cannot assert the latter but we
716
     * can assert the former */
717
0
    IGRAPH_ASSERT(graph != newgraph);
718
0
    if (igraph_i_attribute_table) {
719
0
        return igraph_i_attribute_table->combine_edges(graph, newgraph,
720
0
                merges,
721
0
                comb);
722
0
    } else {
723
0
        return IGRAPH_SUCCESS;
724
0
    }
725
0
}
726
727
igraph_error_t igraph_i_attribute_get_info(const igraph_t *graph,
728
                                igraph_strvector_t *gnames,
729
                                igraph_vector_int_t *gtypes,
730
                                igraph_strvector_t *vnames,
731
                                igraph_vector_int_t *vtypes,
732
                                igraph_strvector_t *enames,
733
0
                                igraph_vector_int_t *etypes) {
734
0
    if (igraph_i_attribute_table) {
735
0
        return igraph_i_attribute_table->get_info(graph, gnames, gtypes,
736
0
                vnames, vtypes,
737
0
                enames, etypes);
738
0
    } else {
739
0
        return IGRAPH_SUCCESS;
740
0
    }
741
0
}
742
743
igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph,
744
        igraph_attribute_elemtype_t type,
745
129k
        const char *name) {
746
129k
    if (igraph_i_attribute_table) {
747
129k
        return igraph_i_attribute_table->has_attr(graph, type, name);
748
129k
    } else {
749
0
        return IGRAPH_SUCCESS;
750
0
    }
751
129k
}
752
753
igraph_error_t igraph_i_attribute_get_type(const igraph_t *graph,
754
                               igraph_attribute_type_t *type,
755
                               igraph_attribute_elemtype_t elemtype,
756
4.70k
                               const char *name) {
757
4.70k
    if (igraph_i_attribute_table) {
758
4.70k
        return igraph_i_attribute_table->get_type(graph, type, elemtype, name);
759
4.70k
    } else {
760
0
        return IGRAPH_SUCCESS;
761
0
    }
762
763
4.70k
}
764
765
igraph_error_t igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph,
766
        const char *name,
767
0
        igraph_vector_t *value) {
768
0
    igraph_vector_clear(value);
769
0
    if (igraph_i_attribute_table) {
770
0
        return igraph_i_attribute_table->get_numeric_graph_attr(graph, name, value);
771
0
    } else {
772
0
        return IGRAPH_SUCCESS;
773
0
    }
774
0
}
775
776
igraph_error_t igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph,
777
        const char *name,
778
        igraph_vs_t vs,
779
3.04M
        igraph_vector_t *value) {
780
3.04M
    igraph_vector_clear(value);
781
3.04M
    if (igraph_i_attribute_table) {
782
3.04M
        return igraph_i_attribute_table->get_numeric_vertex_attr(graph, name, vs, value);
783
3.04M
    } else {
784
0
        return IGRAPH_SUCCESS;
785
0
    }
786
3.04M
}
787
788
igraph_error_t igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph,
789
        const char *name,
790
        igraph_es_t es,
791
4.04M
        igraph_vector_t *value) {
792
4.04M
    igraph_vector_clear(value);
793
4.04M
    if (igraph_i_attribute_table) {
794
4.04M
        return igraph_i_attribute_table->get_numeric_edge_attr(graph, name, es, value);
795
4.04M
    } else {
796
0
        return IGRAPH_SUCCESS;
797
0
    }
798
4.04M
}
799
800
igraph_error_t igraph_i_attribute_get_string_graph_attr(const igraph_t *graph,
801
        const char *name,
802
0
        igraph_strvector_t *value) {
803
0
    igraph_strvector_clear(value);
804
0
    if (igraph_i_attribute_table) {
805
0
        return igraph_i_attribute_table->get_string_graph_attr(graph, name, value);
806
0
    } else {
807
0
        return IGRAPH_SUCCESS;
808
0
    }
809
0
}
810
811
igraph_error_t igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph,
812
        const char *name,
813
        igraph_vs_t vs,
814
3.76M
        igraph_strvector_t *value) {
815
3.76M
    igraph_strvector_clear(value);
816
3.76M
    if (igraph_i_attribute_table) {
817
3.76M
        return igraph_i_attribute_table->get_string_vertex_attr(graph, name, vs, value);
818
3.76M
    } else {
819
0
        return IGRAPH_SUCCESS;
820
0
    }
821
3.76M
}
822
823
igraph_error_t igraph_i_attribute_get_string_edge_attr(const igraph_t *graph,
824
        const char *name,
825
        igraph_es_t es,
826
1.20M
        igraph_strvector_t *value) {
827
1.20M
    igraph_strvector_clear(value);
828
1.20M
    if (igraph_i_attribute_table) {
829
1.20M
        return igraph_i_attribute_table->get_string_edge_attr(graph, name, es, value);
830
1.20M
    } else {
831
0
        return IGRAPH_SUCCESS;
832
0
    }
833
1.20M
}
834
835
igraph_error_t igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph,
836
        const char *name,
837
0
        igraph_vector_bool_t *value) {
838
0
    igraph_vector_bool_clear(value);
839
0
    if (igraph_i_attribute_table) {
840
0
        return igraph_i_attribute_table->get_bool_graph_attr(graph, name, value);
841
0
    } else {
842
0
        return IGRAPH_SUCCESS;
843
0
    }
844
0
}
845
846
igraph_error_t igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph,
847
        const char *name,
848
        igraph_vs_t vs,
849
22.3M
        igraph_vector_bool_t *value) {
850
22.3M
    igraph_vector_bool_clear(value);
851
22.3M
    if (igraph_i_attribute_table) {
852
22.3M
        return igraph_i_attribute_table->get_bool_vertex_attr(graph, name, vs, value);
853
22.3M
    } else {
854
0
        return IGRAPH_SUCCESS;
855
0
    }
856
22.3M
}
857
858
igraph_error_t igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph,
859
        const char *name,
860
        igraph_es_t es,
861
0
        igraph_vector_bool_t *value) {
862
0
    igraph_vector_bool_clear(value);
863
0
    if (igraph_i_attribute_table) {
864
0
        return igraph_i_attribute_table->get_bool_edge_attr(graph, name, es, value);
865
0
    } else {
866
0
        return IGRAPH_SUCCESS;
867
0
    }
868
0
}
869
870
/**
871
 * \function igraph_set_attribute_table
872
 * \brief Attach an attribute table.
873
 *
874
 * This function attaches attribute handling code to the igraph library.
875
 * Note that the attribute handler table is \em not thread-local even if
876
 * igraph is compiled in thread-local mode. In the vast majority of cases,
877
 * this is not a significant restriction.
878
 *
879
 * </para><para>
880
 * Attribute handlers are normally attached on program startup, and are
881
 * left active for the program's lifetime. This is because a graph object
882
 * created with a given attribute handler must not be manipulated while
883
 * a different attribute handler is active.
884
 *
885
 * \param table Pointer to an \ref igraph_attribute_table_t object
886
 *    containing the functions for attribute manipulation. Supply \c
887
 *    NULL here if you don't want attributes.
888
 * \return Pointer to the old attribute handling table.
889
 *
890
 * Time complexity: O(1).
891
 */
892
893
igraph_attribute_table_t *
894
3.79k
igraph_set_attribute_table(const igraph_attribute_table_t * table) {
895
3.79k
    igraph_attribute_table_t *old = igraph_i_attribute_table;
896
3.79k
    igraph_i_attribute_table = (igraph_attribute_table_t*) table;
897
3.79k
    return old;
898
3.79k
}
899
900
0
igraph_bool_t igraph_has_attribute_table(void) {
901
0
    return igraph_i_attribute_table != NULL;
902
0
}
903
904
905
/**
906
 * \function igraph_attribute_combination_init
907
 * \brief Initialize attribute combination list.
908
 *
909
 * \param comb The uninitialized attribute combination list.
910
 * \return Error code.
911
 *
912
 * Time complexity: O(1)
913
 */
914
0
igraph_error_t igraph_attribute_combination_init(igraph_attribute_combination_t *comb) {
915
0
    IGRAPH_CHECK(igraph_vector_ptr_init(&comb->list, 0));
916
0
    return IGRAPH_SUCCESS;
917
0
}
918
919
/**
920
 * \function igraph_attribute_combination_destroy
921
 * \brief Destroy attribute combination list.
922
 *
923
 * \param comb The attribute combination list.
924
 *
925
 * Time complexity: O(n), where n is the number of records in the
926
                    attribute combination list.
927
 */
928
0
void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb) {
929
0
    igraph_integer_t i, n = igraph_vector_ptr_size(&comb->list);
930
0
    for (i = 0; i < n; i++) {
931
0
        igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i];
932
0
        if (rec->name) {
933
0
            IGRAPH_FREE(rec->name);
934
0
        }
935
0
        IGRAPH_FREE(rec);
936
0
    }
937
0
    igraph_vector_ptr_destroy(&comb->list);
938
0
}
939
940
/**
941
 * \function igraph_attribute_combination_add
942
 * \brief Add combination record to attribute combination list.
943
 *
944
 * \param comb The attribute combination list.
945
 * \param name The name of the attribute. If the name already exists
946
 *             the attribute combination record will be replaced.
947
 *             Use NULL to add a default combination record for all
948
 *             atributes not in the list.
949
 * \param type The type of the attribute combination. See \ref
950
 *             igraph_attribute_combination_type_t for the options.
951
 * \param func Function to be used if \p type is
952
 *             \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION. This function is called
953
 *             by the concrete attribute handler attached to igraph, and its
954
 *             calling signature depends completely on the attribute handler.
955
 *             For instance, if you are using attributes from C and you have
956
 *             attached the C attribute handler, you need to follow the
957
 *             documentation of the <link linkend="c-attribute-combination-functions">C attribute handler</link>
958
 *             for more details.
959
 * \return Error code.
960
 *
961
 * Time complexity: O(n), where n is the number of current attribute
962
 *                  combinations.
963
 */
964
igraph_error_t igraph_attribute_combination_add(igraph_attribute_combination_t *comb,
965
                                     const char *name,
966
                                     igraph_attribute_combination_type_t type,
967
0
                                     igraph_function_pointer_t func) {
968
0
    igraph_integer_t i, n = igraph_vector_ptr_size(&comb->list);
969
970
    /* Search, in case it is already there */
971
0
    for (i = 0; i < n; i++) {
972
0
        igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
973
0
        const char *n = r->name;
974
0
        if ( (!name && !n) ||
975
0
             (name && n && !strcmp(n, name)) ) {
976
0
            r->type = type;
977
0
            r->func = func;
978
0
            break;
979
0
        }
980
0
    }
981
982
0
    if (i == n) {
983
        /* This is a new attribute name */
984
0
        igraph_attribute_combination_record_t *rec =
985
0
            IGRAPH_CALLOC(1, igraph_attribute_combination_record_t);
986
0
        if (! rec) {
987
0
            IGRAPH_ERROR("Cannot create attribute combination data.",
988
0
                         IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */
989
0
        }
990
0
        IGRAPH_FINALLY(igraph_free, rec);
991
0
        if (! name) {
992
0
            rec->name = NULL;
993
0
        } else {
994
0
            rec->name = strdup(name);
995
0
            if (! rec->name) {
996
0
                IGRAPH_ERROR("Cannot create attribute combination data.",
997
0
                             IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */
998
0
            }
999
0
        }
1000
0
        IGRAPH_FINALLY(igraph_free, (char *) rec->name); /* free() is safe on NULL */
1001
0
        rec->type = type;
1002
0
        rec->func = func;
1003
1004
0
        IGRAPH_CHECK(igraph_vector_ptr_push_back(&comb->list, rec));
1005
0
        IGRAPH_FINALLY_CLEAN(2); /* ownership of 'rec' transferred to 'comb->list' */
1006
1007
0
    }
1008
1009
0
    return IGRAPH_SUCCESS;
1010
0
}
1011
1012
/**
1013
 * \function igraph_attribute_combination_remove
1014
 * \brief Remove a record from an attribute combination list.
1015
 *
1016
 * \param comb The attribute combination list.
1017
 * \param name The attribute name of the attribute combination record
1018
 *             to remove. It will be ignored if the named attribute
1019
 *             does not exist. It can be NULL to remove the default
1020
 *             combination record.
1021
 * \return Error code. This currently always returns IGRAPH_SUCCESS.
1022
 *
1023
 * Time complexity: O(n), where n is the number of records in the attribute
1024
                    combination list.
1025
 */
1026
igraph_error_t igraph_attribute_combination_remove(igraph_attribute_combination_t *comb,
1027
0
                                        const char *name) {
1028
0
    igraph_integer_t i, n = igraph_vector_ptr_size(&comb->list);
1029
1030
    /* Search, in case it is already there */
1031
0
    for (i = 0; i < n; i++) {
1032
0
        igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
1033
0
        const char *n = r->name;
1034
0
        if ( (!name && !n) ||
1035
0
             (name && n && !strcmp(n, name)) ) {
1036
0
            break;
1037
0
        }
1038
0
    }
1039
1040
0
    if (i != n) {
1041
0
        igraph_attribute_combination_record_t *r = VECTOR(comb->list)[i];
1042
0
        if (r->name) {
1043
0
            IGRAPH_FREE(r->name);
1044
0
        }
1045
0
        IGRAPH_FREE(r);
1046
0
        igraph_vector_ptr_remove(&comb->list, i);
1047
0
    } else {
1048
        /* It is not there, we don't do anything */
1049
0
    }
1050
1051
0
    return IGRAPH_SUCCESS;
1052
0
}
1053
1054
igraph_error_t igraph_attribute_combination_query(const igraph_attribute_combination_t *comb,
1055
                                       const char *name,
1056
                                       igraph_attribute_combination_type_t *type,
1057
0
                                       igraph_function_pointer_t *func) {
1058
0
    igraph_integer_t i, def = -1, len = igraph_vector_ptr_size(&comb->list);
1059
1060
0
    for (i = 0; i < len; i++) {
1061
0
        igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[i];
1062
0
        const char *n = rec->name;
1063
0
        if ( (!name && !n) ||
1064
0
             (name && n && !strcmp(n, name)) ) {
1065
0
            *type = rec->type;
1066
0
            *func = rec->func;
1067
0
            return IGRAPH_SUCCESS;
1068
0
        }
1069
0
        if (!n) {
1070
0
            def = i;
1071
0
        }
1072
0
    }
1073
1074
0
    if (def == -1) {
1075
        /* Did not find anything */
1076
0
        *type = IGRAPH_ATTRIBUTE_COMBINE_DEFAULT;
1077
0
        *func = 0;
1078
0
    } else {
1079
0
        igraph_attribute_combination_record_t *rec = VECTOR(comb->list)[def];
1080
0
        *type = rec->type;
1081
0
        *func = rec->func;
1082
0
    }
1083
1084
0
    return IGRAPH_SUCCESS;
1085
0
}
1086
1087
/**
1088
 * \function igraph_attribute_combination
1089
 * \brief Initialize attribute combination list and add records.
1090
 *
1091
 * \param comb The uninitialized attribute combination list.
1092
 * \param ...  A list of 'name, type[, func]', where:
1093
 * \param name The name of the attribute. If the name already exists
1094
 *             the attribute combination record will be replaced.
1095
 *             Use NULL to add a default combination record for all
1096
 *             atributes not in the list.
1097
 * \param type The type of the attribute combination. See \ref
1098
 *             igraph_attribute_combination_type_t for the options.
1099
 * \param func Function to be used if \p type is
1100
 *             \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION.
1101
 * The list is closed by setting the name to \c IGRAPH_NO_MORE_ATTRIBUTES.
1102
 * \return Error code.
1103
 *
1104
 * Time complexity: O(n^2), where n is the number attribute
1105
 *                  combinations records to add.
1106
 *
1107
 * \example examples/simple/igraph_attribute_combination.c
1108
 */
1109
igraph_error_t igraph_attribute_combination(
1110
0
        igraph_attribute_combination_t *comb, ...) {
1111
1112
0
    va_list ap;
1113
1114
0
    IGRAPH_CHECK(igraph_attribute_combination_init(comb));
1115
1116
0
    va_start(ap, comb);
1117
0
    while (true) {
1118
0
        igraph_function_pointer_t func = NULL;
1119
0
        igraph_attribute_combination_type_t type;
1120
0
        const char *name;
1121
1122
0
        name = va_arg(ap, const char *);
1123
1124
0
        if (name == IGRAPH_NO_MORE_ATTRIBUTES) {
1125
0
            break;
1126
0
        }
1127
1128
0
        type = (igraph_attribute_combination_type_t) va_arg(ap, int);
1129
0
        if (type == IGRAPH_ATTRIBUTE_COMBINE_FUNCTION) {
1130
0
            func = va_arg(ap, igraph_function_pointer_t);
1131
0
        }
1132
1133
0
        if (strlen(name) == 0) {
1134
0
            name = 0;
1135
0
        }
1136
1137
0
        igraph_error_t ret = igraph_attribute_combination_add(comb, name, type, func);
1138
0
        if (ret != IGRAPH_SUCCESS) {
1139
0
            va_end(ap);
1140
0
            return ret;
1141
0
        }
1142
0
    }
1143
1144
0
    va_end(ap);
1145
1146
0
    return IGRAPH_SUCCESS;
1147
0
}