1 /*** Copyright 1989 Norbert Schlenker. All rights reserved.
3 *** This software is distributed subject to the following provisions:
4 *** - this notice may not be removed;
5 *** - you may modify the source code, as long as redistributed
6 *** versions have their modifications clearly marked;
7 *** - no charge, other than a nominal copying fee, may be made
8 *** when providing copies of this source code to others;
9 *** - if this source code is used as part of a product which is
10 *** distributed only as a binary, a copy of this source code
11 *** must be included in the distribution.
13 *** Unlike the GNU GPL, use of this code does not obligate you to
14 *** disclose your own proprietary source code.
16 *** The author of this software provides no warranty, express or
17 *** implied, as to its utility or correctness. That said, reports
18 *** of bugs or compatibility problems will be gladly received by
19 *** nfs@princeton.edu, and fixes will be attempted.
23 /*** index.c - routines to manipulate index lists */
25 /*** Definition: An "index list" is an ascending sequence of positive
26 *** integers that can be operated upon as follows:
28 *** make_index_list - makes an index list of consecutive
29 *** integers from some lower bound through an upper bound.
30 *** choose_index - given a number "k", removes the integer
31 *** in the k'th position in the index list and returns it.
32 *** Requests for positions less than 1 or greater than
33 *** the current list length return zero.
34 *** remove_index - removes a specified integer from the
35 *** index list. Requests to remove integers not in the
36 *** list are ignored, except that they reduce the list's
37 *** "pseudo_size" (see below).
38 *** index_size - returns the number of integers in the
40 *** pseudo_size - returns the number of integers in the
41 *** index list, less the number for which remove_index
42 *** failed due to a request to remove a non-existent one.
43 *** (Note that this is here solely to support an apparent
44 *** bug in the original definition of the NETGEN program.)
46 *** Two simple methods of accomplishing these functions are:
47 *** - allocating an array of flags that indicate whether a particular
48 *** integer is valid, and searching the array during the choose_index
49 *** operation for the k'th valid integer.
50 *** - allocating a linked list for the indices and updating the list
51 *** during both the choose_index and remove_index operations.
53 *** For small index lists, the first of these methods is quite efficient
54 *** and is, in fact, implemented in the following code. Unfortunately,
55 *** for the uses we have in mind (i.e. NETGEN), the typical access pattern
56 *** to index lists involves a large list, with both choose_index and
57 *** remove_index operations occurring at random positions in the list.
59 *** As a result, the code has been extended for the case of large lists.
60 *** The enclosed implementation makes use of a binary interval tree, which
61 *** records information regarding the valid intervals from which indices
62 *** may be chosen. At a cost of a substantial increase in space requirements,
63 *** and under rather generous assumptions regarding the randomness of the
64 *** positions supplied to choose_index, running time becomes logarithmic
65 *** per choose_index and remove_index operation.
70 /*** Useful constants */
71 #define FLAG_LIMIT 100 /* upper limit for simple implementation */
74 /*** Internally useful types */
75 typedef unsigned char FLAG;
77 typedef struct index_header {
78 INDEX original_size; /* original size of index */
79 INDEX index_size; /* number of indices in the index */
80 INDEX pseudo_size; /* almost the number of indices in the index */
82 INDEX index_base; /* base of index list - small case */
83 INDEX index_nodes; /* number of nodes in the interval tree - large case */
86 FLAG* flag; /* pointer to flag array - small */
87 struct interval_node* first_node; /* pointer to root of interval tree - large */
91 typedef struct interval_node {
92 INDEX base; /* smallest integer in this subtree */
93 INDEX count; /* count of indices in this subtree */
94 struct interval_node* left_child; /* pointers down the tree */
98 /*** Static storage */
100 static INDEX_LIST active_handles = 0;
101 static HEADER* index_headers = NULL;
104 /*** Make a new index list with a specified range. Returns an integer handle
105 *** to identify the list, or -1 if an error occurs.
107 INDEX_LIST make_index_list(from, to)
108 INDEX from; /* lower limit of index list */
109 INDEX to; /* upper limit of index list */
111 INDEX_LIST handle = 0;
115 if (from <= 0 || from > to) /* sanity check */
118 /*** Find an inactive list header or allocate a new one. */
119 for (hp = index_headers; handle < active_handles; hp++, handle++) {
120 if (hp->original_size == 0)
123 if (handle == active_handles) {
126 index_headers = (HEADER*) malloc(active_handles * sizeof(HEADER));
128 index_headers = (HEADER*) realloc(index_headers, active_handles * sizeof(HEADER));
130 if (index_headers == NULL)
134 /*** Fill in the list header and allocate space for the list. */
135 hp = &index_headers[handle];
136 hp->pseudo_size = hp->index_size = hp->original_size = to - from + 1;
137 if (hp->original_size <= FLAG_LIMIT) { /* SMALL */
138 hp->i.index_base = from;
139 hp->p.flag = (FLAG*) malloc(hp->original_size * sizeof(FLAG));
140 if (hp->p.flag == NULL)
142 (void)memset((void *)hp->p.flag, 0, hp->original_size * sizeof(FLAG));
144 hp->i.index_nodes = 1;
145 np = (INODE*) malloc(hp->original_size * sizeof(INODE));
148 hp->p.first_node = np;
150 np->count = hp->original_size;
151 np->left_child = NULL;
157 /*** Free an existing index list. The header is zeroed afterwards
158 *** to indicate that it can be reused.
160 void free_index_list(handle)
165 if (handle < 0 || handle >= active_handles) /* sanity check */
168 hp = &index_headers[handle];
170 free((void *)hp->p.flag);
171 (void)memset((void *)hp, 0, sizeof(HEADER));
174 /*** Choose the integer at a certain position in an index list. The
175 *** integer is then removed from the list so that it won't be chosen
176 *** again. Choose_index returns 0 if the position is invalid.
178 INDEX choose_index(handle, position)
189 if (handle < 0 || handle >= active_handles) /* sanity checks */
191 hp = &index_headers[handle];
192 if (hp->p.flag == NULL)
194 if (position < 1 || position > hp->index_size)
197 /*** Adjust counts of remaining indices. */
202 /*** Find the index we want and remove it from the list. */
203 if (hp->original_size <= FLAG_LIMIT) { /* SMALL */
204 for (cp = hp->p.flag; position > 0; cp++)
208 return hp->i.index_base + (INDEX)(cp - hp->p.flag);
210 np = hp->p.first_node;
211 while (np->left_child) {
214 if (position > np->count) {
215 position -= np->count;
220 if (position == 1) { /* beginning of interval */
224 if (position > np->count) { /* end of interval */
225 index = np->base + np->count;
227 else /* middle of interval - split it */
229 index = np->base + position - 1;
230 npl = np->left_child = hp->p.first_node + hp->i.index_nodes;
232 hp->i.index_nodes += 2;
233 npl->base = np->base;
234 npl->count = position - 1;
235 npl->left_child = NULL;
236 npr->base = index + 1;
237 npr->count = np->count - npl->count;
238 npr->left_child = NULL;
244 /*** Remove a particular integer from an index list. If the integer
245 *** does not exist in the list, we reduce the list's pseudo-size,
246 *** but return no error indication.
248 void remove_index(handle, index)
258 if (handle < 0 || handle >= active_handles) /* sanity checks */
260 hp = &index_headers[handle];
261 if (hp->p.flag == NULL)
264 /*** Adjust the pseudo-size before looking for the index. */
267 /*** Remove the index from the index list. */
268 if (hp->original_size <= FLAG_LIMIT) { /* SMALL */
269 if (index < hp->i.index_base || index >= hp->i.index_base + hp->original_size)
271 cp = hp->p.flag + (index - hp->i.index_base);
278 np = hp->p.first_node;
279 while (np->left_child) {
281 np = np->left_child + 1;
282 if (index < np->base)
285 if (index < np->base || index >= np->base + np->count) { /* mistake - back out */
286 np = hp->p.first_node;
287 while (np->left_child) {
289 np = np->left_child + 1;
290 if (index < np->base)
296 if (index == np->base) { /* beginning of interval */
300 if (index == np->base + np->count) { /* end of interval */
302 else /* middle of interval - split it */
304 npl = np->left_child = hp->p.first_node + hp->i.index_nodes;
306 hp->i.index_nodes += 2;
307 npl->base = np->base;
308 npl->count = index - np->base;
309 npl->left_child = NULL;
310 npr->base = index + 1;
311 npr->count = np->count - npl->count;
312 npr->left_child = NULL;
320 /*** Return actual number of remaining entries in the index list.
322 INDEX index_size(handle)
325 if (handle < 0 || handle >= active_handles) /* sanity check */
328 return index_headers[handle].index_size;
332 /*** Return a peculiar number, vaguely related to the number of
333 *** remaining entries in the index list. Required by NETGEN.
335 INDEX pseudo_size(handle)
338 if (handle < 0 || handle >= active_handles) /* sanity check */
341 return index_headers[handle].pseudo_size;