generators/netgen/index.c
changeset 8 3c30bd0f9ccc
equal deleted inserted replaced
-1:000000000000 0:261f39bb2f27
       
     1 /*** Copyright 1989 Norbert Schlenker.  All rights reserved.
       
     2 
       
     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.
       
    12  ***
       
    13  *** Unlike the GNU GPL, use of this code does not obligate you to
       
    14  *** disclose your own proprietary source code.
       
    15 
       
    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.
       
    20  ***/
       
    21 
       
    22 
       
    23 /*** index.c - routines to manipulate index lists */
       
    24 
       
    25 /*** Definition:  An "index list" is an ascending sequence of positive
       
    26  ***              integers that can be operated upon as follows:
       
    27  ***
       
    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
       
    39  ***                    index list.
       
    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.)
       
    45 
       
    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.
       
    52  ***
       
    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.
       
    58  ***
       
    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.
       
    66  ***/
       
    67 
       
    68 #include "netgen.h"
       
    69 
       
    70 /*** Useful constants */
       
    71 #define FLAG_LIMIT 100		/* upper limit for simple implementation */
       
    72 
       
    73 
       
    74 /*** Internally useful types */
       
    75 typedef unsigned char FLAG;
       
    76 
       
    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 */
       
    81   union {
       
    82     INDEX index_base;		/* base of index list - small case */
       
    83     INDEX index_nodes;		/* number of nodes in the interval tree - large case */
       
    84   } i;
       
    85   union {
       
    86     FLAG* flag;			/* pointer to flag array - small */
       
    87     struct interval_node* first_node; /* pointer to root of interval tree - large */
       
    88   } p;
       
    89 } HEADER;
       
    90 
       
    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 */
       
    95 } INODE;
       
    96 
       
    97 
       
    98 /*** Static storage */
       
    99 
       
   100 static INDEX_LIST active_handles = 0;
       
   101 static HEADER* index_headers = NULL;
       
   102 
       
   103 
       
   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.
       
   106  ***/
       
   107 INDEX_LIST make_index_list(from, to)
       
   108 INDEX from;			/* lower limit of index list */
       
   109 INDEX to;			/* upper limit of index list */
       
   110 {
       
   111   INDEX_LIST handle = 0;
       
   112   HEADER* hp;
       
   113   INODE* np;
       
   114 
       
   115   if (from <= 0 || from > to)	/* sanity check */
       
   116     return -1;
       
   117 
       
   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)
       
   121       break;
       
   122   }
       
   123   if (handle == active_handles) {
       
   124     ++active_handles;
       
   125     if (handle == 0)
       
   126       index_headers = (HEADER*) malloc(active_handles * sizeof(HEADER));
       
   127     else
       
   128       index_headers = (HEADER*) realloc(index_headers, active_handles * sizeof(HEADER));
       
   129   }
       
   130   if (index_headers == NULL)
       
   131     return -1;
       
   132 
       
   133 
       
   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)
       
   141       return -1;
       
   142     (void)memset((void *)hp->p.flag, 0, hp->original_size * sizeof(FLAG));
       
   143   } else {			/* LARGE */
       
   144     hp->i.index_nodes = 1;
       
   145     np = (INODE*) malloc(hp->original_size * sizeof(INODE));
       
   146     if (np == NULL)
       
   147       return -1;
       
   148     hp->p.first_node = np;
       
   149     np->base = from;
       
   150     np->count = hp->original_size;
       
   151     np->left_child = NULL;
       
   152   }
       
   153   return handle;
       
   154 }
       
   155 
       
   156 
       
   157 /*** Free an existing index list.  The header is zeroed afterwards
       
   158  *** to indicate that it can be reused.
       
   159  ***/
       
   160 void free_index_list(handle)
       
   161 INDEX_LIST handle;
       
   162 {
       
   163   HEADER* hp;
       
   164 
       
   165   if (handle < 0 || handle >= active_handles)	/* sanity check */
       
   166     return;
       
   167 
       
   168   hp = &index_headers[handle];
       
   169   if (hp->p.flag)
       
   170     free((void *)hp->p.flag);
       
   171   (void)memset((void *)hp, 0, sizeof(HEADER));
       
   172 }
       
   173 
       
   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.
       
   177  ***/
       
   178 INDEX choose_index(handle, position)
       
   179 INDEX_LIST handle;
       
   180 INDEX position;
       
   181 {
       
   182   HEADER* hp;
       
   183   FLAG* cp;
       
   184   INODE* np;
       
   185   INODE* npl;
       
   186   INODE* npr;
       
   187   INDEX index;
       
   188 
       
   189   if (handle < 0 || handle >= active_handles)	/* sanity checks */
       
   190     return 0;
       
   191   hp = &index_headers[handle];
       
   192   if (hp->p.flag == NULL)
       
   193     return 0;
       
   194   if (position < 1 || position > hp->index_size)
       
   195     return 0;
       
   196 
       
   197 /*** Adjust counts of remaining indices. */
       
   198   hp->index_size--;
       
   199   hp->pseudo_size--;
       
   200 
       
   201 
       
   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++)
       
   205       if (!*cp)
       
   206 	position--;
       
   207     *(--cp) = 1;
       
   208     return hp->i.index_base + (INDEX)(cp - hp->p.flag);
       
   209   } else {			/* LARGE */
       
   210     np = hp->p.first_node;
       
   211     while (np->left_child) {
       
   212       np->count--;
       
   213       np = np->left_child;
       
   214       if (position > np->count) {
       
   215 	position -= np->count;
       
   216 	np++;
       
   217       }
       
   218     }
       
   219     np->count--;
       
   220     if (position == 1) {	/* beginning of interval */
       
   221       index = np->base++;
       
   222     }
       
   223     else
       
   224     if (position > np->count) {	/* end of interval */
       
   225       index = np->base + np->count;
       
   226     }
       
   227     else			/* middle of interval - split it */
       
   228     {
       
   229       index = np->base + position - 1;
       
   230       npl = np->left_child = hp->p.first_node + hp->i.index_nodes;
       
   231       npr = npl + 1;
       
   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;
       
   239     }
       
   240     return index;
       
   241   }
       
   242 }
       
   243 
       
   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.
       
   247  ***/
       
   248 void remove_index(handle, index)
       
   249 INDEX_LIST handle;
       
   250 INDEX index;
       
   251 {
       
   252   HEADER* hp;
       
   253   FLAG* cp;
       
   254   INODE* np;
       
   255   INODE* npl;
       
   256   INODE* npr;
       
   257 
       
   258   if (handle < 0 || handle >= active_handles)	/* sanity checks */
       
   259     return;
       
   260   hp = &index_headers[handle];
       
   261   if (hp->p.flag == NULL)
       
   262     return;
       
   263 
       
   264 /*** Adjust the pseudo-size before looking for the index. */
       
   265   hp->pseudo_size--;
       
   266 
       
   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)
       
   270       return;
       
   271     cp = hp->p.flag + (index - hp->i.index_base);
       
   272     if (!*cp) {
       
   273       *cp = 1;
       
   274       hp->index_size--;
       
   275     }
       
   276     return;
       
   277   } else {			/* LARGE */
       
   278     np = hp->p.first_node;
       
   279     while (np->left_child) {
       
   280       np->count--;
       
   281       np = np->left_child + 1;
       
   282       if (index < np->base)
       
   283 	np--;
       
   284     }
       
   285     if (index < np->base || index >= np->base + np->count) { /* mistake - back out */
       
   286       np = hp->p.first_node;
       
   287       while (np->left_child) {
       
   288         np->count++;
       
   289 	np = np->left_child + 1;
       
   290 	if (index < np->base)
       
   291 	  np--;
       
   292       }
       
   293       return;
       
   294     }
       
   295     np->count--;
       
   296     if (index == np->base) {			/* beginning of interval */
       
   297       np->base++;
       
   298     }
       
   299     else
       
   300     if (index == np->base + np->count) {	/* end of interval */
       
   301     }
       
   302     else    	    	    			/* middle of interval - split it */
       
   303     {
       
   304       npl = np->left_child = hp->p.first_node + hp->i.index_nodes;
       
   305       npr = npl + 1;
       
   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;
       
   313     }
       
   314     hp->index_size--;
       
   315     return;
       
   316   }
       
   317 }
       
   318 
       
   319 
       
   320 /*** Return actual number of remaining entries in the index list.
       
   321  ***/
       
   322 INDEX index_size(handle)
       
   323 INDEX_LIST handle;
       
   324 {
       
   325   if (handle < 0 || handle >= active_handles)	/* sanity check */
       
   326     return 0;
       
   327 
       
   328   return index_headers[handle].index_size;
       
   329 }
       
   330 
       
   331 
       
   332 /*** Return a peculiar number, vaguely related to the number of
       
   333  *** remaining entries in the index list.  Required by NETGEN.
       
   334  ***/
       
   335 INDEX pseudo_size(handle)
       
   336 INDEX_LIST handle;
       
   337 {
       
   338   if (handle < 0 || handle >= active_handles)	/* sanity check */
       
   339     return 0;
       
   340 
       
   341   return index_headers[handle].pseudo_size;
       
   342 }