Add a search_atleast operation.
[gnulib.git] / lib / gl_array_oset.c
1 /* Ordered set data type implemented by an array.
2    Copyright (C) 2006 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2006.
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, or (at your option)
8    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, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 #include <config.h>
20
21 /* Specification.  */
22 #include "gl_array_oset.h"
23
24 #include <stdlib.h>
25
26 #include "xalloc.h"
27
28 /* Checked size_t computations.  */
29 #include "xsize.h"
30
31 /* -------------------------- gl_oset_t Data Type -------------------------- */
32
33 /* Concrete gl_oset_impl type, valid for this file only.  */
34 struct gl_oset_impl
35 {
36   struct gl_oset_impl_base base;
37   /* An array of ALLOCATED elements, of which the first COUNT are used.
38      0 <= COUNT <= ALLOCATED.  */
39   const void **elements;
40   size_t count;
41   size_t allocated;
42 };
43
44 static gl_oset_t
45 gl_array_create_empty (gl_oset_implementation_t implementation,
46                        gl_setelement_compar_fn compar_fn)
47 {
48   struct gl_oset_impl *set =
49     (struct gl_oset_impl *) xmalloc (sizeof (struct gl_oset_impl));
50
51   set->base.vtable = implementation;
52   set->base.compar_fn = compar_fn;
53   set->elements = NULL;
54   set->count = 0;
55   set->allocated = 0;
56
57   return set;
58 }
59
60 static size_t
61 gl_array_size (gl_oset_t set)
62 {
63   return set->count;
64 }
65
66 static size_t
67 gl_array_indexof (gl_oset_t set, const void *elt)
68 {
69   size_t count = set->count;
70
71   if (count > 0)
72     {
73       gl_setelement_compar_fn compar = set->base.compar_fn;
74       size_t low = 0;
75       size_t high = count;
76
77       /* At each loop iteration, low < high; for indices < low the values
78          are smaller than ELT; for indices >= high the values are greater
79          than ELT.  So, if the element occurs in the list, it is at
80          low <= position < high.  */
81       do
82         {
83           size_t mid = low + (high - low) / 2; /* low <= mid < high */
84           int cmp = (compar != NULL
85                      ? compar (set->elements[mid], elt)
86                      : (set->elements[mid] > elt ? 1 :
87                         set->elements[mid] < elt ? -1 : 0));
88
89           if (cmp < 0)
90             low = mid + 1;
91           else if (cmp > 0)
92             high = mid;
93           else /* cmp == 0 */
94             /* We have an element equal to ELT at index MID.  */
95             return mid;
96         }
97       while (low < high);
98     }
99   return (size_t)(-1);
100 }
101
102 static bool
103 gl_array_search (gl_oset_t set, const void *elt)
104 {
105   return gl_array_indexof (set, elt) != (size_t)(-1);
106 }
107
108 static bool
109 gl_array_search_atleast (gl_oset_t set,
110                          gl_setelement_threshold_fn threshold_fn,
111                          const void *threshold,
112                          const void **eltp)
113 {
114   size_t count = set->count;
115
116   if (count > 0)
117     {
118       size_t low = 0;
119       size_t high = count;
120
121       /* At each loop iteration, low < high; for indices < low the values are
122          smaller than THRESHOLD; for indices >= high the values are nonexistent.
123          So, if an element >= THRESHOLD occurs in the list, it is at
124          low <= position < high.  */
125       do
126         {
127           size_t mid = low + (high - low) / 2; /* low <= mid < high */
128
129           if (! threshold_fn (set->elements[mid], threshold))
130             low = mid + 1;
131           else
132             {
133               /* We have an element >= THRESHOLD at index MID.  But we need the
134                  minimal such index.  */
135               high = mid;
136               /* At each loop iteration, low <= high and
137                    compar (list->elements[high], value) >= 0,
138                  and we know that the first occurrence of the element is at
139                  low <= position <= high.  */
140               while (low < high)
141                 {
142                   size_t mid2 = low + (high - low) / 2; /* low <= mid2 < high */
143
144                   if (! threshold_fn (set->elements[mid2], threshold))
145                     low = mid2 + 1;
146                   else
147                     high = mid2;
148                 }
149               *eltp = set->elements[low];
150               return true;
151             }
152         }
153       while (low < high);
154     }
155   return false;
156 }
157
158 /* Ensure that set->allocated > set->count.  */
159 static void
160 grow (gl_oset_t set)
161 {
162   size_t new_allocated;
163   size_t memory_size;
164   const void **memory;
165
166   new_allocated = xtimes (set->allocated, 2);
167   new_allocated = xsum (new_allocated, 1);
168   memory_size = xtimes (new_allocated, sizeof (const void *));
169   if (size_overflow_p (memory_size))
170     /* Overflow, would lead to out of memory.  */
171     xalloc_die ();
172   memory = (const void **) xrealloc (set->elements, memory_size);
173   if (memory == NULL)
174     /* Out of memory.  */
175     xalloc_die ();
176   set->elements = memory;
177   set->allocated = new_allocated;
178 }
179
180 /* Add the given element ELT at the given position,
181    0 <= position <= gl_oset_size (set).  */
182 static inline void
183 gl_array_add_at (gl_oset_t set, size_t position, const void *elt)
184 {
185   size_t count = set->count;
186   const void **elements;
187   size_t i;
188
189   if (count == set->allocated)
190     grow (set);
191   elements = set->elements;
192   for (i = count; i > position; i--)
193     elements[i] = elements[i - 1];
194   elements[position] = elt;
195   set->count = count + 1;
196 }
197
198 /* Remove the element at the given position,
199    0 <= position < gl_oset_size (set).  */
200 static inline void
201 gl_array_remove_at (gl_oset_t set, size_t position)
202 {
203   size_t count = set->count;
204   const void **elements;
205   size_t i;
206
207   elements = set->elements;
208   for (i = position + 1; i < count; i++)
209     elements[i - 1] = elements[i];
210   set->count = count - 1;
211 }
212
213 static bool
214 gl_array_add (gl_oset_t set, const void *elt)
215 {
216   size_t count = set->count;
217   size_t low = 0;
218
219   if (count > 0)
220     {
221       gl_setelement_compar_fn compar = set->base.compar_fn;
222       size_t high = count;
223
224       /* At each loop iteration, low < high; for indices < low the values
225          are smaller than ELT; for indices >= high the values are greater
226          than ELT.  So, if the element occurs in the list, it is at
227          low <= position < high.  */
228       do
229         {
230           size_t mid = low + (high - low) / 2; /* low <= mid < high */
231           int cmp = (compar != NULL
232                      ? compar (set->elements[mid], elt)
233                      : (set->elements[mid] > elt ? 1 :
234                         set->elements[mid] < elt ? -1 : 0));
235
236           if (cmp < 0)
237             low = mid + 1;
238           else if (cmp > 0)
239             high = mid;
240           else /* cmp == 0 */
241             return false;
242         }
243       while (low < high);
244     }
245   gl_array_add_at (set, low, elt);
246   return true;
247 }
248
249 static bool
250 gl_array_remove (gl_oset_t set, const void *elt)
251 {
252   size_t index = gl_array_indexof (set, elt);
253   if (index != (size_t)(-1))
254     {
255       gl_array_remove_at (set, index);
256       return true;
257     }
258   else
259     return false;
260 }
261
262 static void
263 gl_array_free (gl_oset_t set)
264 {
265   if (set->elements != NULL)
266     free (set->elements);
267   free (set);
268 }
269
270 /* --------------------- gl_oset_iterator_t Data Type --------------------- */
271
272 static gl_oset_iterator_t
273 gl_array_iterator (gl_oset_t set)
274 {
275   gl_oset_iterator_t result;
276
277   result.vtable = set->base.vtable;
278   result.set = set;
279   result.count = set->count;
280   result.p = set->elements + 0;
281   result.q = set->elements + set->count;
282 #ifdef lint
283   result.i = 0;
284   result.j = 0;
285 #endif
286
287   return result;
288 }
289
290 static bool
291 gl_array_iterator_next (gl_oset_iterator_t *iterator, const void **eltp)
292 {
293   gl_oset_t set = iterator->set;
294   if (iterator->count != set->count)
295     {
296       if (iterator->count != set->count + 1)
297         /* Concurrent modifications were done on the set.  */
298         abort ();
299       /* The last returned element was removed.  */
300       iterator->count--;
301       iterator->p--;
302       iterator->q--;
303     }
304   if (iterator->p < iterator->q)
305     {
306       const void **p = (const void **) iterator->p;
307       *eltp = *p;
308       iterator->p = p + 1;
309       return true;
310     }
311   else
312     return false;
313 }
314
315 static void
316 gl_array_iterator_free (gl_oset_iterator_t *iterator)
317 {
318 }
319
320
321 const struct gl_oset_implementation gl_array_oset_implementation =
322   {
323     gl_array_create_empty,
324     gl_array_size,
325     gl_array_search,
326     gl_array_search_atleast,
327     gl_array_add,
328     gl_array_remove,
329     gl_array_free,
330     gl_array_iterator,
331     gl_array_iterator_next,
332     gl_array_iterator_free
333   };