generators/netgen/index.c
author Alpar Juttner <alpar@cs.elte.hu>
Sun, 11 Dec 2011 06:55:47 +0100
changeset 9 7768d68909e8
permissions -rw-r--r--
Circulation test code added
     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 }