/src/igraph/src/core/set.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | IGraph library. |
3 | | Copyright (C) 2006-2024 The igraph development team <igraph@igraph.org> |
4 | | |
5 | | This program is free software; you can redistribute it and/or modify |
6 | | it under the terms of the GNU General Public License as published by |
7 | | the Free Software Foundation; either version 2 of the License, or |
8 | | (at your option) any later version. |
9 | | |
10 | | This program is distributed in the hope that it will be useful, |
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, see <https://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #include "igraph_memory.h" |
20 | | |
21 | | #include "core/set.h" |
22 | | |
23 | | #include <string.h> /* memmove */ |
24 | | |
25 | 0 | #define SET(s) ((s).stor_begin) |
26 | | |
27 | | /** |
28 | | * \ingroup set |
29 | | * \function igraph_set_init |
30 | | * \brief Initializes a set. |
31 | | * |
32 | | * Initializes an empty set (with zero elements). Allocates memory for |
33 | | * the requested capacity. No re-allocation will be necessary until the |
34 | | * number of elements exceeds this initial capacity. |
35 | | * |
36 | | * \param set Pointer to the set to be initialized. |
37 | | * \param capacity The expected number of elements in the set. |
38 | | * |
39 | | * \return error code: |
40 | | * \c IGRAPH_ENOMEM if there is not enough memory. |
41 | | * |
42 | | * Time complexity: operating system dependent, should be around |
43 | | * O(n), n is the expected size of the set. |
44 | | */ |
45 | 0 | igraph_error_t igraph_set_init(igraph_set_t *set, igraph_integer_t capacity) { |
46 | 0 | igraph_integer_t alloc_size; |
47 | |
|
48 | 0 | IGRAPH_ASSERT(capacity >= 0); |
49 | 0 | alloc_size = capacity > 0 ? capacity : 1; |
50 | 0 | set->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); |
51 | 0 | if (! set->stor_begin) { |
52 | 0 | IGRAPH_ERROR("Cannot initialize set.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ |
53 | 0 | } |
54 | 0 | set->stor_end = set->stor_begin + alloc_size; |
55 | 0 | set->end = set->stor_begin; |
56 | |
|
57 | 0 | return IGRAPH_SUCCESS; |
58 | 0 | } |
59 | | |
60 | | /** |
61 | | * \ingroup set |
62 | | * \function igraph_set_destroy |
63 | | * \brief Destroys a set object. |
64 | | * |
65 | | * \param set Pointer to the set to be destroyed. |
66 | | * |
67 | | * Time complexity: operating system dependent. |
68 | | */ |
69 | 0 | void igraph_set_destroy(igraph_set_t *set) { |
70 | 0 | IGRAPH_ASSERT(set != NULL); |
71 | 0 | if (set->stor_begin != NULL) { |
72 | 0 | IGRAPH_FREE(set->stor_begin); /* sets to NULL */ |
73 | 0 | } |
74 | 0 | } |
75 | | |
76 | | /** |
77 | | * \ingroup set |
78 | | * \function igraph_set_inited |
79 | | * \brief Determines whether a set is initialized or not. |
80 | | * |
81 | | * This function checks whether the internal storage for the members of the |
82 | | * set has been allocated or not, and it assumes that the pointer for the |
83 | | * internal storage area contains \c NULL if the area is not initialized yet. |
84 | | * This only applies if you have allocated an array of sets with \c IGRAPH_CALLOC or |
85 | | * if you used the \c IGRAPH_SET_NULL constant to initialize the set. |
86 | | * |
87 | | * \param set The set object. |
88 | | * |
89 | | * Time complexity: O(1) |
90 | | */ |
91 | 0 | igraph_bool_t igraph_set_inited(igraph_set_t *set) { |
92 | 0 | return (set->stor_begin != NULL); |
93 | 0 | } |
94 | | |
95 | | /** |
96 | | * \ingroup set |
97 | | * \function igraph_set_reserve |
98 | | * \brief Reserves memory for a set. |
99 | | * |
100 | | * \param set The set object. |
101 | | * \param capacity the new \em allocated capacity of the set. |
102 | | * |
103 | | * Time complexity: operating system dependent, should be around |
104 | | * O(n), n is the new allocated size of the set. |
105 | | */ |
106 | 0 | igraph_error_t igraph_set_reserve(igraph_set_t *set, igraph_integer_t capacity) { |
107 | 0 | igraph_integer_t actual_size = igraph_set_size(set); |
108 | 0 | igraph_integer_t *tmp; |
109 | 0 | IGRAPH_ASSERT(set != NULL); |
110 | 0 | IGRAPH_ASSERT(set->stor_begin != NULL); |
111 | 0 | if (capacity <= actual_size) { |
112 | 0 | return IGRAPH_SUCCESS; |
113 | 0 | } |
114 | | |
115 | 0 | tmp = IGRAPH_REALLOC(set->stor_begin, capacity, igraph_integer_t); |
116 | 0 | IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for set."); |
117 | | |
118 | 0 | set->stor_begin = tmp; |
119 | 0 | set->stor_end = set->stor_begin + capacity; |
120 | 0 | set->end = set->stor_begin + actual_size; |
121 | |
|
122 | 0 | return IGRAPH_SUCCESS; |
123 | 0 | } |
124 | | |
125 | | /** |
126 | | * \ingroup set |
127 | | * \function igraph_set_empty |
128 | | * \brief Decides whether the size of the set is zero. |
129 | | * |
130 | | * \param set The set object. |
131 | | * \return True if the size of the set is not zero and false otherwise. |
132 | | * |
133 | | * Time complexity: O(1). |
134 | | */ |
135 | 0 | igraph_bool_t igraph_set_empty(const igraph_set_t *set) { |
136 | 0 | IGRAPH_ASSERT(set != NULL); |
137 | 0 | IGRAPH_ASSERT(set->stor_begin != NULL); |
138 | 0 | return set->stor_begin == set->end; |
139 | 0 | } |
140 | | |
141 | | /** |
142 | | * \ingroup set |
143 | | * \function igraph_set_clear |
144 | | * \brief Removes all elements from the set. |
145 | | * |
146 | | * This function simply sets the size of the set to zero, it does |
147 | | * not free any allocated memory. For that you have to call |
148 | | * |
149 | | * \ref igraph_set_destroy(). |
150 | | * |
151 | | * \param set The set object. |
152 | | * |
153 | | * Time complexity: O(1). |
154 | | */ |
155 | 0 | void igraph_set_clear(igraph_set_t *set) { |
156 | 0 | IGRAPH_ASSERT(set != NULL); |
157 | 0 | IGRAPH_ASSERT(set->stor_begin != NULL); |
158 | 0 | set->end = set->stor_begin; |
159 | 0 | } |
160 | | |
161 | | |
162 | | /** |
163 | | * \ingroup set |
164 | | * \function igraph_set_size |
165 | | * \brief Gives the size of the set. |
166 | | * |
167 | | * The number of elements in the set. |
168 | | * |
169 | | * \param set The set object |
170 | | * \return The size of the set. |
171 | | * |
172 | | * Time complexity: O(1). |
173 | | */ |
174 | | |
175 | 0 | igraph_integer_t igraph_set_size(const igraph_set_t *set) { |
176 | 0 | IGRAPH_ASSERT(set != NULL); |
177 | 0 | IGRAPH_ASSERT(set->stor_begin != NULL); |
178 | 0 | return set->end - set->stor_begin; |
179 | 0 | } |
180 | | |
181 | | |
182 | | /** |
183 | | * \ingroup set |
184 | | * \function igraph_set_add |
185 | | * \brief Adds an element to the set. |
186 | | * |
187 | | * \param set The set object. |
188 | | * \param e The element to be added. |
189 | | * \return Error code: |
190 | | * \c IGRAPH_ENOMEM: not enough memory. |
191 | | * |
192 | | * Time complexity: O(log(n)), n is the number of elements in \p set. |
193 | | */ |
194 | 0 | igraph_error_t igraph_set_add(igraph_set_t *set, igraph_integer_t e) { |
195 | 0 | igraph_integer_t left, right, middle; |
196 | 0 | igraph_integer_t size; |
197 | 0 | IGRAPH_ASSERT(set != NULL); |
198 | 0 | IGRAPH_ASSERT(set->stor_begin != NULL); |
199 | | |
200 | 0 | size = igraph_set_size(set); |
201 | | |
202 | | /* search where to insert the new element */ |
203 | 0 | left = 0; |
204 | 0 | right = size - 1; |
205 | 0 | while (left < right - 1) { |
206 | 0 | middle = (left + right) / 2; |
207 | 0 | if (SET(*set)[middle] > e) { |
208 | 0 | right = middle; |
209 | 0 | } else if (SET(*set)[middle] < e) { |
210 | 0 | left = middle; |
211 | 0 | } else { |
212 | 0 | left = middle; |
213 | 0 | break; |
214 | 0 | } |
215 | 0 | } |
216 | |
|
217 | 0 | if (right >= 0 && SET(*set)[left] != e && SET(*set)[right] == e) { |
218 | 0 | left = right; |
219 | 0 | } |
220 | |
|
221 | 0 | while (left < size && set->stor_begin[left] < e) { |
222 | 0 | left++; |
223 | 0 | } |
224 | 0 | if (left >= size || set->stor_begin[left] != e) { |
225 | | /* full, allocate more storage */ |
226 | 0 | if (set->stor_end == set->end) { |
227 | 0 | igraph_integer_t new_size = size < IGRAPH_INTEGER_MAX/2 ? size * 2 : IGRAPH_INTEGER_MAX; |
228 | 0 | if (size == IGRAPH_INTEGER_MAX) { |
229 | 0 | IGRAPH_ERROR("Cannot add to set, already at maximum size.", IGRAPH_EOVERFLOW); |
230 | 0 | } |
231 | 0 | if (new_size == 0) { |
232 | 0 | new_size = 1; |
233 | 0 | } |
234 | 0 | IGRAPH_CHECK(igraph_set_reserve(set, new_size)); |
235 | 0 | } |
236 | | |
237 | | /* Element should be inserted at position 'left' */ |
238 | 0 | if (left < size) |
239 | 0 | memmove(set->stor_begin + left + 1, set->stor_begin + left, |
240 | 0 | (size - left) * sizeof(set->stor_begin[0])); |
241 | |
|
242 | 0 | set->stor_begin[left] = e; |
243 | 0 | set->end += 1; |
244 | 0 | } |
245 | | |
246 | 0 | return IGRAPH_SUCCESS; |
247 | 0 | } |
248 | | |
249 | | /** |
250 | | * \ingroup set |
251 | | * \function igraph_set_contains |
252 | | * \brief Checks whether a given element is in the set or not. |
253 | | * |
254 | | * \param set The set object. |
255 | | * \param e The element being sought. |
256 | | * \return True if \p e is found, false otherwise. |
257 | | * |
258 | | * Time complexity: O(log(n)), n is the number of elements in \p set. |
259 | | */ |
260 | 0 | igraph_bool_t igraph_set_contains(const igraph_set_t *set, igraph_integer_t e) { |
261 | 0 | igraph_integer_t left, right, middle; |
262 | |
|
263 | 0 | IGRAPH_ASSERT(set != NULL); |
264 | 0 | IGRAPH_ASSERT(set->stor_begin != NULL); |
265 | | |
266 | 0 | left = 0; |
267 | 0 | right = igraph_set_size(set) - 1; |
268 | |
|
269 | 0 | if (right == -1) { |
270 | 0 | return false; /* the set is empty */ |
271 | 0 | } |
272 | | |
273 | | /* search for the new element */ |
274 | 0 | while (left < right - 1) { |
275 | 0 | middle = (left + right) / 2; |
276 | 0 | if (SET(*set)[middle] > e) { |
277 | 0 | right = middle; |
278 | 0 | } else if (SET(*set)[middle] < e) { |
279 | 0 | left = middle; |
280 | 0 | } else { |
281 | 0 | return true; |
282 | 0 | } |
283 | 0 | } |
284 | | |
285 | 0 | return SET(*set)[left] == e || SET(*set)[right] == e; |
286 | 0 | } |
287 | | |
288 | | /** |
289 | | * \ingroup set |
290 | | * \function igraph_set_iterate |
291 | | * \brief Iterates through the element of the set. |
292 | | * |
293 | | * Elements are returned in an arbitrary order. |
294 | | * |
295 | | * \param set The set object. |
296 | | * \param state Internal state of the iteration. |
297 | | * This should be a pointer to a \type igraph_integer_t variable |
298 | | * which must be zero for the first invocation. |
299 | | * The object must not be adjusted and its value should |
300 | | * not be used for anything during the iteration. |
301 | | * \param element The next element or 0 (if the iteration |
302 | | * has ended) is returned here. |
303 | | * |
304 | | * \return True if there are more elements, false otherwise. |
305 | | */ |
306 | | igraph_bool_t igraph_set_iterate(const igraph_set_t *set, igraph_integer_t *state, |
307 | 0 | igraph_integer_t *element) { |
308 | 0 | IGRAPH_ASSERT(set != 0); |
309 | 0 | IGRAPH_ASSERT(set->stor_begin != 0); |
310 | 0 | IGRAPH_ASSERT(state != 0); |
311 | 0 | IGRAPH_ASSERT(element != 0); |
312 | | |
313 | 0 | if (*state < igraph_set_size(set)) { |
314 | 0 | *element = set->stor_begin[*state]; |
315 | 0 | *state = *state + 1; |
316 | 0 | return true; |
317 | 0 | } else { |
318 | 0 | *element = 0; |
319 | 0 | return false; |
320 | 0 | } |
321 | 0 | } |