/*
* numlist.h
* by Ben Allan
* December 20, 1997
* Part of ASCEND
* Version: $Revision: 1.6 $
* Version control file: $RCSfile: numlist.c,v $
* Date last modified: $Date: 1998/06/16 16:38:43 $
* Last modified by: $Author: mthomas $
*
* This file is part of the Ascend Language Interpreter.
*
* Copyright (C) 1998 Carnegie Mellon University
*
* The Ascend Language Interpreter is free software; you can
* redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* The Ascend Language Interpreter is distributed in hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include "numlist.h" /* should be in general/ */
#if NUMLISTUSESPOOL
#include
#endif
/*
* This is an element in a list of
* numbers and ranges.
* Valid numbers are 1..GL_INT_MAX.
* Numbers are stored in increasing order.
* Valid numbers are 1..GL_INT_MAX.
* numpair is a range which include all numbers from .lo to .hi
* including the limits of the range. A range may go from i to i.
* but not from j to i where j >i.
*/
struct numpair {
GLint lo, hi;
};
/*
* These are the objects we hand out to the user.
* but they don't need to know that.
*/
struct numpair_list {
struct numpair_list *head;
struct numpair *list; /* array of pairs */
/* if not NULL, list is piece of core rooted in list of head.
* if NULL this list's data is referenced by refcount pointers.
* When refcount is 0, destroying the numpair_list
* should entail destroying its data and head list as well.
* The g_recycled_npl may reference a list, as will all the
* lists created using the data of this list.
*/
GLint len; /* size of list, meaning data in use */
GLint cap; /* capacity of allocated list, if allocated, else -1. */
GLint refcount; /* number of sharers in data space if head is NULL. */
};
struct shared_data {
struct numpair_list *headlist;/* list allocated bigger than needed. */
int headcap; /* size of allocation in headlist */
int headfree; /* elements in headlist data at end
* not in use. */
};
/*
* List of head lists with space available.
* Cap will grow to the size needed.
* Size will shrink to zero cap/NULL when len -> 0.
*/
static struct {
struct shared_data *shared; /* array of shared list records */
int shared_cap ; /* array size. */
int shared_len ; /* array size in use */
} g_recycled_npl = {NULL,0,0};
#define GRN g_recycled_npl /* cheesy alias */
#define GROW_GRN 0.5 /* grow by this fraction of previous size */
#define INITSIZE_GRN 100
/*
* set the largest number of elements we are willing to
* sacrifice when declaring a shared data entirely used up.
* must be at least 1. If having short headfree values
* causes a long search for free space, then increase minfree.
*/
#define MINFREE 1
/*
* set the number of elements to put in a shared head list.
* should be reasonably large. Memory will be allocated in
* blocks of SHRSIZE*sizeof(struct numpair) when creating
* shareable data spaces.
* All lists larger than this will be individually created.
*/
#define SHRSIZE 1024
/*
* in shared lists, we will stick a checknum in between
* shared data chunks. If this is overwritten, someone
* has overrun a list.
*/
#define NHCHECKNUM GL_INT_MAX
/*
* we should pool the heads.
*/
#if NUMLISTUSESPOOL
static pool_store_t g_numlist_head_pool = NULL;
/* global for our memory manager */
/* aim for 4096 chunks including malloc overhead */
#define PNL_LEN 10
#ifdef __alpha
#define PNL_WID 100
#else
#define PNL_WID 201
#endif
/* retune rpwid if the size of struct numlist changes */
#define PNL_ELT_SIZE (sizeof(struct numpair_list))
#define PNL_MORE_ELTS 10
/*
* Number of slots filled if more elements needed.
* So if the pool grows, it grows by PNL_MORE_ELTS*PNL_WID elements at a time.
*/
#define PNL_MORE_BARS 500
/* This is the number of pool bar slots to add during expansion.
not all the slots will be filled immediately. */
/* This function is called at compiler startup time and destroy at shutdown. */
static
void numlist_init_pool(void) {
if (g_numlist_head_pool != NULL ) {
ASC_PANIC("ERROR: numlist_init_pool called twice.\n");
}
g_numlist_head_pool = pool_create_store(PNL_LEN, PNL_WID, PNL_ELT_SIZE,
PNL_MORE_ELTS, PNL_MORE_BARS);
if (g_numlist_head_pool == NULL) {
ASC_PANIC("ERROR: numlist_init_pool unable to allocate pool.\n");
}
}
static
void numlist_destroy_pool(void) {
if (g_numlist_head_pool==NULL) return;
pool_clear_store(g_numlist_head_pool);
pool_destroy_store(g_numlist_head_pool);
g_numlist_head_pool = NULL;
}
#ifdef THIS_IS_AN_UNUSED_FUNCTION
/*@unused@*/
static
void numlist_report_pool(FILE *f)
{
if (g_numlist_head_pool==NULL) {
FPRINTF(f,"NumlistHeadPool is empty\n");
}
FPRINTF(f,"NumlistHeadPool ");
pool_print_store(f,g_numlist_head_pool,0);
}
#endif
static
struct numpair_list *GetPoolHead()
{
if (g_numlist_head_pool == NULL) {
numlist_init_pool();
}
return (struct numpair_list *)(pool_get_element(g_numlist_head_pool));
}
#define NHMALLOC GetPoolHead()
/* get a token. Token is the size of the struct struct numpair_list */
#define NHFREE(p) (pool_free_element(g_numlist_head_pool,((void *)p)))
/* return a struct numpair_list */
#else
#define NHFREE(p) ascfree(p)
#define NHMALLOC ASC_NEW(struct numpair_list)
#endif
/* write np up to len to stderr */
static
void NPWrite(FILE *fp, struct numpair *p, int len)
{
int i;
if (fp == NULL) {
fp = stderr;
}
if (p == NULL) {
return;
}
FPRINTF(fp,"0x%p: ",p);
for (i = 0; i < len; i++) {
if (p[i].lo != p[i].hi) {
FPRINTF(fp,"%d..%d, ", p[i].lo, p[i].hi);
} else {
FPRINTF(fp,"%d, ",p[i].hi);
}
}
FPRINTF(fp,"\n");
return;
}
/* conditionally exported */
void NLPWrite(FILE *fp, Numlist_p nlp)
{
if (nlp == NULL) {
return;
}
NPWrite(fp,nlp->list,nlp->len);
if (fp == NULL) {
fp = stderr;
}
FPRINTF(fp,"len = %d, cap = %d\n",nlp->len,nlp->cap);
}
/*
* nlp = NumpairExpandableList(nlp,size);
* Expands the size of nlp, which must be created
* with NumpairExpandableList. If nlp is NULL, creates
* the list with the size specified.
* If insufficient memory to create or expand to size
* required, returns NULL.
*/
Numlist_p NumpairExpandableList(Numlist_p nlp, GLint newsize)
{
struct numpair *newdata;
if (nlp == NULL) {
if (newsize == 0) {
return NULL;
} else {
nlp = NHMALLOC;
if (nlp == NULL) {
return NULL; /* not reached */
}
}
nlp->list = ASC_NEW_ARRAY(struct numpair,newsize);
if (nlp->list == NULL) {
NHFREE(nlp);
return NULL;
}
nlp->refcount = 1;
nlp->len = 0;
nlp->cap = newsize;
nlp->head = NULL;
} else {
if (nlp->head != NULL || nlp->refcount != 1 || nlp->cap == -1) {
Asc_Panic(2,"Numlist_p to be expanded is not expandable",
"NumpairExpandableList");
return NULL; /*not reached */
}
if (nlp->cap >= newsize) {
return nlp;
}
}
newdata = (struct numpair *)ascrealloc(nlp->list,
sizeof(struct numpair)*newsize);
if (newdata == NULL) {
return NULL;
} else {
nlp->list = newdata;
nlp->cap = newsize;
return nlp;
}
}
/*
* Destroy a list. list may have come from
* NumpairCopyList or NumpairExpandableList.
*/
void NumpairDestroyList(Numlist_p nlp)
{
if (nlp == NULL) {
return;
}
/* nlp sharing data space it doesn't own */
if (nlp->head != NULL) {
#ifndef NDEBUG
/* check for overrun */
if (nlp->list !=NULL) {
if (nlp->list[-1].lo != NHCHECKNUM || nlp->list[-1].hi != NHCHECKNUM) {
Asc_Panic(2,"Write past end of numlist detected",
"NumpairDestroyList");
/* not reached */
}
}
#endif
nlp->head->refcount--;
if (nlp->head->refcount == 0 ) {
/* this reference may have kept its nlp around
* after the head was destroyed officially.
*/
ascfree(nlp->head->list);
nlp->head->refcount = -3;
NHFREE(nlp->head);
}
assert(nlp->refcount == 1);
nlp->refcount = -2;
NHFREE(nlp);
return;
}
/* this owns a data space that may be shared */
nlp->refcount--;
if (nlp->refcount < 1) {
/* nobody else cares about data, so destroy all */
ascfree(nlp->list);
nlp->refcount = -2;
NHFREE(nlp);
}
}
/* called only by numpaircopylist.
* copy a list. data capacity will be dlen, but list
* result will only know its size of nlp->len.
* Result is potentially to become shared data space of size dlen,
* so someone other than
* the list is responsible for tracking capacity.
* At any rate, the result of this function is not expandable, appendable.
*/
static
Numlist_p NumpairDuplicate(Numlist_p nlp, GLint dlen)
{
Numlist_p result;
struct numpair *data, *src;
GLint len,i;
assert(nlp != NULL);
result = NHMALLOC;
if (result == NULL) {
return NULL;
}
len = nlp->len;
result->list = ASC_NEW_ARRAY(struct numpair,dlen);
result->head = NULL;
if (result->list == NULL) {
NHFREE(result);
return NULL;
}
result->refcount = 1;
result->len = len;
/* result->cap = dlen; somebody else's problem */
result->cap = -1; /* how odd! but this means list is not expandable */
data = result->list;
src = nlp->list;
for (i=0;i < len; i++) {
data[i].lo = src[i].lo;
data[i].hi = src[i].hi;
}
return result;
}
/*
* find a list in GRN.shared which is big enough to hold need
* data. return -1 if none found.
*/
static
int GetHead(GLint need)
{
GLint len,i;
len = GRN.shared_len;
i = 0;
while (ilen;
nlp->refcount++;
}
/* return a singleton list */
Numlist_p NumpairElementary(GLint num)
{
struct numpair_list nl;
struct numpair np;
/* fake list not allocated, the use copy operator */
nl.len = 1;
nl.list = &np;
nl.head = NULL;
nl.refcount=0;
np.lo = np.hi = num;
return NumpairCopyList(&nl);
}
/*
* nlp2 = NumpairCopyList(nlp);
* Numlist_p nlp2, nlp;
* Returns an efficiently allocated numpair_list containing the
* data of the list given. The data in this list may or may not
* be in a shared allocation, depending on the list size.
*/
Numlist_p NumpairCopyList(Numlist_p nlp)
{
Numlist_p head, result;
struct numpair *data, *src;
GLint len,i, cell,nextelt;
assert(nlp != NULL);
/* if oversized list. create, copy, return */
if (nlp->len >= SHRSIZE - MINFREE) {
return NumpairDuplicate(nlp,nlp->len);
}
/* list we'd like to copy by using data space at end of some other list.
* Get index of head with shareable data. if not available, make one and
* add it to puddle of shared lists.
*/
cell = GetHead(nlp->len+1); /* we're putting in the checknum so 1 more */
if (cell != -1) {
len = nlp->len;
/* get elements needed from just after end of data in head */
head = GRN.shared[cell].headlist;
nextelt = GRN.shared[cell].headcap - GRN.shared[cell].headfree;
data = &(head->list[nextelt]);
/* deduct from remaining in cell */
GRN.shared[cell].headfree -= (len+1);
if (GRN.shared[cell].headfree < MINFREE) {
/* eliminate reference at shared[cell] and decrease list len. */
head->refcount--;
if (cell < GRN.shared_len-1) {
/* copy tail to replace cell, if cell isn't tail */
GRN.shared[cell] = GRN.shared[GRN.shared_len-1];
}
GRN.shared_len--;
if ( GRN.shared_len ==0) {
ascfree(GRN.shared);
GRN.shared = NULL;
GRN.shared_cap = 0;
}
}
result = NHMALLOC;
if (result == NULL) {
return NULL;
}
data[0].lo = NHCHECKNUM;
data[0].hi = NHCHECKNUM;
data++; /* skip past pad */
result->list = data;
result->head = head;
result->refcount = 1;
head->refcount++;
src = nlp->list;
for (i=0;i < len; i++) {
data[i].lo = src[i].lo;
data[i].hi = src[i].hi;
}
result->len = len;
result->cap = -1;
return result;
} else {
/* create list, copy data to it, add list to shared pool,
* and return resulting list.
*/
result = NumpairDuplicate(nlp,SHRSIZE);
if (result != NULL) {
AddToGRN(result,SHRSIZE);
}
return result;
}
/* not reached */
}
/* third arg is a pair of long, not a pointer.
* assumes p.lo >= r[rlen-1].lo
*/
static
GLint ExtendResult(struct numpair *r, CONST GLint rlen, CONST struct numpair p)
{
GLint idx;
if (rlen) {
idx = rlen - 1;
if (p.lo > r[idx].hi+1) {
/* add new disjoint element to r. len++ */
r[rlen].lo = p.lo;
r[rlen].hi = p.hi;
return rlen + 1;
}
/* extend range to p.hi if needed. no len change. */
if (p.hi > r[idx].hi) {
r[idx].hi = p.hi;
}
return rlen;
} else {
/* first ever element */
r[0].lo = p.lo;
r[0].hi = p.hi;
return 1;
}
}
/*
* err = NumpairMergeLists(list1,list2,result);
* struct numpair_list *list1, *list2, *result;
* The maximum needed size of result is list1->len+list2->len.
* The size of result is not checked and this will
* corrupt memory if result needed is larger than result given.
* Result must be expandable. list1,list2 need not be.
* Result is overwritten even if it already has nonzero length.
*/
static
void NumpairMergeLists(Numlist_p nlp1, Numlist_p nlp2, Numlist_p nlpr)
{
GLint n1,len1, n2,len2,lenr; /* lengths of lists */
struct numpair *p1, *p2, *r; /* data from lists */
len1 = nlp1->len;
len2 = nlp2->len;
p1 = nlp1->list;
p2 = nlp2->list;
r = nlpr->list;
n1 = n2 = lenr = 0;
while (n1 < len1 && n2 < len2) {
/* copy all p1 entries starting before p2 entry */
while (n1 < len1 && p1[n1].lo <= p2[n2].lo) {
lenr = ExtendResult(r,lenr,p1[n1]);
n1++;
}
if (n1 == len1) {
break;
}
/* copy all p2 entries starting before p1 entry */
while (n2 < len2 && p2[n2].lo < p1[n1].lo) {
lenr = ExtendResult(r,lenr,p2[n2]);
n2++;
}
}
/* clean up leftovers. only one of the following while loops will go */
while (n1 < len1) {
lenr = ExtendResult(r,lenr,p1[n1]);
n1++;
}
while (n2 < len2) {
lenr = ExtendResult(r,lenr,p2[n2]);
n2++;
}
nlpr->len = lenr;
}
/*
* NumpairCalcUnion(enlp1,nlp2,enlp3);
* Numlist_p enlp1,nlp2,enlp3;
* Calculates the union of enlp1, nlp2 and leaves the result in
* enlp1. enlp3 is used if needed and is left in an indeterminate
* state.
*/
void NumpairCalcUnion(Numlist_p enlp1, Numlist_p nlp2, Numlist_p enlp3)
{
Numlist_p t;
struct numpair *src, *data; /* data from lists */
GLint len, i;
assert(enlp1 != NULL);
assert(enlp3 != NULL);
assert(nlp2 != NULL);
assert(enlp1 != enlp3);
/* handle case of no previous data in enlp1 */
if (enlp1->len == 0) {
t = NumpairExpandableList(enlp1, nlp2->len);
if (t == NULL) { /* ack! */
return;
}
data = enlp1->list;
src = nlp2->list;
len = nlp2->len;
enlp1->len = len;
for (i=0;i < len; i++) {
data[i].lo = src[i].lo;
data[i].hi = src[i].hi;
}
return; /* simple case done */
}
/* make sure we have memory for worst case */
t = NumpairExpandableList(enlp3, enlp1->len + nlp2->len);
if (t==NULL) { /* ack! */
return;
}
NumpairMergeLists(enlp1, nlp2, enlp3);
t = NumpairExpandableList(enlp1, enlp3->len);
if (t==NULL) { /* ack! */
return;
}
/* copy back data to enlp1 */
data = enlp1->list;
src = enlp3->list;
len = enlp3->len;
enlp1->len = len;
for (i=0;i < len; i++) {
data[i].lo = src[i].lo;
data[i].hi = src[i].hi;
}
}
void NumpairCalcIntersection(Numlist_p nlp1, Numlist_p nlp2, Numlist_p enlp3)
{
struct numpair *s1, *s2, *r; /* data from lists */
struct numpair overlap;
GLint i, j, lenr;
GLint l1,l2;
assert(nlp1 != NULL);
assert(nlp2 != NULL);
assert(enlp3 != NULL);
assert(nlp1 != enlp3);
assert(nlp2 != enlp3);
l1 = nlp1->len;
l2 = nlp2->len;
s1 = nlp1->list;
s2 = nlp2->list;
NumpairClearList(enlp3);
/* skip the nobrainers */
if (!l1 ||
!l2 ||
s2[0].lo > s1[l1-1].hi ||
s1[0].lo > s2[l2-1].hi) {
return;
}
NumpairExpandableList(enlp3, l1 + l2);
if (s1==NULL) {
Asc_Panic(2,"Numlist_p enlp3 to be expanded. Insufficient memory.",
"NumpairCalcIntersection");
}
lenr = i = j = 0;
while (j < l2 && i < l1) {
/* could we test elsewhere for i,j and break? */
while (i < l1 && s1[i].hi < s2[j].lo) {
/* move i up to next j it might overlap */
i++;
}
if (i >= l1) {
/* ran out of data */
break;
}
while (j < l2 && s2[j].hi < s1[i].lo) {
/* move j up to next i it might overlap */
j++;
}
if (j >= l2) {
/* ran out of data */
break;
}
/* max lo1,lo2 */
overlap.lo = (s1[i].lo > s2[j].lo) ? s1[i].lo : s2[j].lo;
/* min hi1, hi2, and note which to move next, or both */
if (s1[i].hi < s2[j].hi) {
overlap.hi = s1[i].hi;
i++;
} else {
overlap.hi = s2[j].hi;
if (s1[i].hi == s2[j++].hi) {
/* hi1 == hi2, move both i j up */
i++;
}
}
if (overlap.hi >= overlap.lo) {
NumpairExpandableList(enlp3, enlp3->len + 1);
r = enlp3->list;
lenr = ExtendResult(r,lenr,overlap);
} /* else the search ahead switched tracks */
}
enlp3->len = lenr;
}
/*
* merges multiple lists efficiently and returns an
* efficient copy of the result.
*/
Numlist_p NumpairCombineLists(struct gl_list_t *gl,
Numlist_p elp1, Numlist_p elp2)
{
unsigned long glen, next;
Numlist_p nlp1,nlp2, tmp, s1,t2;
assert(gl!=NULL);
assert(elp1!=NULL);
assert(elp2!=NULL);
assert(elp1!=elp2);
assert(elp1->cap >= 0);
assert(elp2->cap >= 0);
glen = gl_length(gl);
switch (glen) {
case 0:
return NULL;
case 1:
return NumpairCopyList((Numlist_p)gl_fetch(gl,1));
default:
break;
}
/* at least 2 lists */
nlp1 = (Numlist_p)gl_fetch(gl,1);
nlp2 = (Numlist_p)gl_fetch(gl,2);
s1 = elp1 = NumpairExpandableList(elp1, nlp1->len + nlp2->len);
if (s1==NULL) {
Asc_Panic(2,"Numlist_p s1 to be expanded. Insufficient memory.",
"NumpairCombineLists");
}
NumpairClearList(s1);
NumpairMergeLists(nlp1,nlp2,s1);
t2 = elp2;
next = 3;
/* on the first trip through the loop, elp2 is the target and elp1 source.
* then they are swapped.
* on the second trip through elp1 is the target, elp2 source.
* each trip through the loop also includes the next nlp.
* on loop exit, s1 is always points to the most recent target.
*/
while (next <= glen) {
nlp1 = (Numlist_p)gl_fetch(gl,next);
t2 = NumpairExpandableList(t2, nlp1->len + s1->len);
if (t2==NULL) {
Asc_Panic(2,"Numlist_p t2 to be expanded. Insufficient memory.",
"NumpairCombineLists");
}
NumpairClearList(t2);
NumpairMergeLists(nlp1,s1,t2);
tmp = s1;
s1 = t2;
t2 = tmp;
next++;
}
return NumpairCopyList(s1);
}
/*
* We trust that no number difference is actually ever > GL_INT_MAX.
* This is a loose comparator. One argument must be a singleton
* with lo =0 and hi the value. This is the key value we want
* to know is in the list or not.
* The other must be a normal element i..i or i..j,j>i.
* This is for use with bsearch.
*/
static
nlbool CmpNumpairs(CONST void *c1, CONST void *c2)
{
CONST struct numpair *n1, *n2;
assert(c1!=NULL);
assert(c2!=NULL);
n1 = (struct numpair *)c1;
n2 = (struct numpair *)c2;
if (n1->lo == 0) {
assert(n2->lo != 0);
/* n1 single, n2 range */
if (n1->hi < n2->lo) {
return -1;
}
if (n1->hi > n2->hi) {
return 1;
}
return 0; /* within range */
} else {
/* n1 range, n2 single */
assert(n2->lo == 0);
if (n2->hi < n1->lo) {
return 1;
}
if (n2->hi > n1->hi) {
return -1;
}
return 0; /* within range */
}
}
/* want to return -1 if num > n2, 1 if num lo != 0);
/* n2 normal range */
if (num < n2->lo) {
return 1;
}
if (num > n2->hi) {
return -1;
}
return 0; /* within range */
}
/* add a number somewhere into the list (sorted order)
* list must be expandable.
*/
void NumpairAppendList(Numlist_p enlp, GLint num)
{
Numlist_p nnlp;
struct numpair *data;
nlbool comparison;
UGLint lower,upper,search=0;
assert(enlp!=NULL);
assert(enlp->cap >=0);
/* expand list if potentially needed */
nnlp = NumpairExpandableList(enlp, enlp->len+1);
if (nnlp == NULL) {
Asc_Panic(2,"Numlist_p to be appended. Insufficient memory.",
"NumpairAppendList");
return; /* not reached */
}
data = enlp->list;
/* no data case */
if (enlp->len==0) {
data[0].lo = data[0].hi = num;
enlp->len = 1;
return;
}
/* boundary cases */
/* low */
if (num < data[0].lo) {
if (num+1 == data[0].lo) {
/* extend lower end */
data[0].lo = num;
return;
} else {
/* insert at 0 */
for (upper = enlp->len; upper > 0; upper--) {
data[upper].lo = data[upper-1].lo;
data[upper].hi = data[upper-1].hi;
}
data[0].lo = data[0].hi = num;
enlp->len++;
return;
}
}
upper = enlp->len - 1;
/* high */
if (num > data[upper].hi) {
if (num-1 == data[upper].hi) {
/* extend upper end */
data[upper].hi = num;
return;
} else {
/* append */
data[enlp->len].lo = data[enlp->len].hi = num;
enlp->len++;
return;
}
}
/* prepend and append eliminated */
/* num belongs on the interior of the list */
lower = 0;
while (lower <= upper) {
search = (lower + upper) >> 1; /* integer divide by two */
comparison = CmpIntToPair(num,&data[search]);
if (comparison == 0) {
/* nothing to do -- got it in the list already */
return;
}
if (comparison < 0) {
lower = search + 1;
} else {
upper = search - 1;
}
}
/* that we got here means num is not in list and that
* lower > upper. num goes between the two remaining
* elements somehow or on an end.
* So we have several cases to handle:
* We are inserting on or between L:num:H
* a) L.hi << num << H.lo --> insert element.
* b) L.hi +1 == num << H.lo --> update L.hi
* c) L.hi << num == H.lo -1 --> update H.lo
* d) L.hi +1 == num == H.lo -1 --> assign H.hi to L.hi, delete H, shift.
*/
/* swap lower and upper so we can read the code. */
/* --> lower == upper-- */
search = upper;
upper = lower;
lower = search;
if (data[lower].hi+1 == num) {
/* b,d */
if (data[upper].lo - 1 != num) {
data[lower].hi = num; /*b*/
} else {
data[lower].hi = data[upper].hi; /* merge adjacent cells, d */
/* shift cells on right 1 to left */
search = (--(enlp->len));
for ( ; upper < search; upper++) {
data[upper].lo = data[upper+1].lo;
data[upper].hi = data[upper+1].hi;
}
}
return;
} else {
/* a,c */
if (data[upper].lo - 1 != num) {
/*a, insert element */
for (search = enlp->len; search > upper; search--) {
data[search].lo = data[search-1].lo;
data[search].hi = data[search-1].hi;
}
data[upper].lo = data[upper].hi = num;
enlp->len++;
} else {
data[upper].lo = num; /*c*/
}
return;
}
}
GLint NumpairListLen(Numlist_p nlp)
{
assert(nlp!=NULL);
return nlp->len;
}
void NumpairClearList(Numlist_p enlp)
{
assert(enlp != NULL);
assert(enlp->cap >= 0);
enlp->len = 0;
}
/*
* NumberInList(nlp,number);
* Returns 1 if number is in list and 0 if it is not.
*/
nlbool NumpairNumberInList(Numlist_p nlp, GLint num)
{
switch (nlp->len) {
case 0:
return 0;
case 1:
if (num < nlp->list[0].lo || num > nlp->list[0].hi) {
return 0;
} else {
return 1;
}
case 2:
if (num < nlp->list[0].lo || num > nlp->list[1].hi) {
return 0;
}
if (num <= nlp->list[0].hi || num >= nlp->list[1].lo) {
return 1;
} else {
return 0;
}
case 3:
if (num < nlp->list[0].lo || num > nlp->list[2].hi) {
return 0;
}
/* num is now within combined range of the 3 elements */
if (num <= nlp->list[0].hi || num >= nlp->list[2].lo) {
/* num is in element 0 or 2 */
return 1;
}
/* num is in element 1 or not in list. */
if (num < nlp->list[1].lo || num > nlp->list[1].hi) {
return 0;
} else {
return 1;
}
default: {
struct numpair test;
char *location; /* location of answer, but we only care if NULL or not */
test.lo = 0;
test.hi = num;
location = bsearch(&test,nlp->list,nlp->len,sizeof(struct numpair),
CmpNumpairs);
return (location != NULL);
}
}
}
/*
* NumberInListHinted(nlp,number,hint);
* Returns 1 if number is in list and 0 if it is not.
*/
static
nlbool InListDecreasingHint(Numlist_p nlp, GLint num, GLint *hint)
{
GLint np;
struct numpair *list;
np = *hint;
assert(np < nlp->len);
list = nlp->list;
if (np < 0) {
np = nlp->len - 1;
}
/* linear search left from np to 0 for a range with lo <= num */
while (np > 0 && list[np].lo > num) {
np--;
}
/* now we may have fallen off low-end of list. so do
* complete check whether num is in the range list[np].
*/
*hint = np;
if (num < list[np].lo || num > list[np].hi) {
return 0;
} else {
return 1;
}
}
/*
* NumberInListHintedDecreasing(nlp,number,hint);
* Returns 1 if number is in list at or to the left of
* hint for lists bigger than 3. hint is ignored for
* small lists. To initiate a series of searches, call
* with *hint == -1.
* Cost O(len) per call worst case, but O(1) if used
* properly.
* Note that if hint value is incorrect, this may lie.
*/
nlbool NumpairNumberInListHintedDecreasing(Numlist_p nlp, GLint num, GLint *hint)
{
assert(hint != NULL);
switch (nlp->len) {
case 0:
return 0;
case 1:
if (num < nlp->list[0].lo || num > nlp->list[0].hi) {
return 0;
} else {
return 1;
}
case 2:
if (num < nlp->list[0].lo || num > nlp->list[1].hi) {
return 0;
}
if (num <= nlp->list[0].hi || num >= nlp->list[1].lo) {
return 1;
} else {
return 0;
}
case 3:
if (num < nlp->list[0].lo || num > nlp->list[2].hi) {
return 0;
}
/* num is now within combined range of the 3 elements */
if (num <= nlp->list[0].hi || num >= nlp->list[2].lo) {
/* num is in element 0 or 2 */
return 1;
}
/* num is in element 1 or not in list. */
if (num < nlp->list[1].lo || num > nlp->list[1].hi) {
return 0;
} else {
return 1;
}
default:
return InListDecreasingHint(nlp,num,hint);
}
}
/*
* prev = NumpairPrevNumber(nlp,last,hint);
* GLint *hint;
* GLint last;
* Returns the next lower number in the list preceding
* last. If last is 0, returns highest
* number in the list. *hint should be the output from the
* last call to this function on nlp, or -1. This function lets
* you write a list iteration. If last given is 0, hint ignored.
* Remember that 0 is never really a valid list element.
*/
GLint NumpairPrevNumber(Numlist_p nlp, GLint last, GLint *hint)
{
struct numpair test;
char *location;
if (last < 1) {
*hint = nlp->len - 1;
return nlp->list[*hint].hi;
}
if (*hint < 0 || *hint >= nlp->len) {
/* given hint is junk. */
*hint = nlp->len - 1;
}
if (nlp->list[*hint].hi < last) {
/* so-so hint type 1*/
(*hint)++;
if (*hint < nlp->len) {
test.lo = 0;
test.hi = last;
location = bsearch(&test,&(nlp->list[*hint]),nlp->len - (*hint),
sizeof(struct numpair), CmpNumpairs);
if (location == NULL) {
*hint = -1;
return 0;
}
*hint = (((struct numpair *)location) - nlp->list);
assert(*hint < nlp->len);
} else {
/* last wasn't in this list. user is an idiot */
*hint = -1;
return 0;
}
} else {
if (nlp->list[*hint].lo > last) {
/* so-so hint type 2*/
(*hint)--;
if (*hint > -1) {
test.lo = 0;
test.hi = last;
location = bsearch(&test, nlp->list, (*hint)+1,
sizeof(struct numpair), CmpNumpairs);
if (location == NULL) {
*hint = -1;
return 0;
}
*hint = (((struct numpair *)location) - nlp->list);
assert(*hint < nlp->len);
} else {
/* last wasn't in this list. user is an idiot */
*hint = -1;
return 0;
}
}
}
/* so at last * hint is on the element of list where last was found */
if (nlp->list[*hint].lo < last) {
return last - 1; /* hint stays same. in middle of range */
}
if (*hint > 0) {
/* move to previous range */
(*hint)--;
return nlp->list[*hint].hi;
} else {
*hint = -1;
return 0;
}
}
/*
* prev = NumpairNextNumber(nlp,last,hint);
* GLint *hint;
* GLint last;
* Returns the next higher number in the list following
* last. If last is >= end of list, returns 0.
* *hint should be the output from the
* last call to this function on nlp, or 0. This function lets
* you write a list iteration. If last 0, hint ignored.
* Remember that 0 is never really a valid list element.
*/
GLint NumpairNextNumber(Numlist_p nlp, GLint last, GLint *hint)
{
struct numpair test;
char *location;
if (last < 1) {
*hint = 0;
return nlp->list[*hint].lo;
}
if (*hint < 0 || *hint >= nlp->len) {
/* given hint is junk. */
*hint = 0;
}
if (nlp->list[*hint].lo > last) {
/* so-so hint type 1*/
(*hint)--;
if (*hint > -1) {
test.lo = 0;
test.hi = last;
location = bsearch(&test,nlp->list, *hint,
sizeof(struct numpair), CmpNumpairs);
if (location == NULL) {
*hint = -1;
return 0;
}
*hint = (((struct numpair *)location) - nlp->list);
assert(*hint < nlp->len);
} else {
/* last wasn't in this list. user is an idiot */
*hint = -1;
return 0;
}
} else {
if (nlp->list[*hint].hi < last) {
/* so-so hint type 2*/
(*hint)++;
if (*hint >= nlp->len) {
test.lo = 0;
test.hi = last;
location = bsearch(&test, &(nlp->list[*hint]), nlp->len - (*hint),
sizeof(struct numpair), CmpNumpairs);
if (location == NULL) {
*hint = -1;
return 0;
}
*hint = (((struct numpair *)location) - nlp->list);
assert(*hint < nlp->len);
} else {
/* last wasn't in this list. user is an idiot */
*hint = -1;
return 0;
}
}
}
/* so at last * hint is on the element of list where last was found */
if (nlp->list[*hint].hi > last) {
return last + 1; /* hint stays same. in middle of range */
}
if (*hint < (nlp->len - 1)) {
/* move to next range */
(*hint)++;
return nlp->list[*hint].lo;
} else {
*hint = -1;
return 0;
}
}
void NumpairListIterate(Numlist_p nlp,NPLFunc func,void *userdata)
{
GLint r,i, rlen,ihi;
if (nlp == NULL || func == NULL || nlp->len == 0) {
return;
}
for (r = 0, rlen = nlp->len; r < rlen; r++) {
for (i = nlp->list[r].lo, ihi = nlp->list[r].hi; i <= ihi; i++) {
(*func)(i,userdata);
}
}
}
/*
* common = NumpairGTIntersection(nlp1,nlp2,lowlimit);
* GLint lowlimit;
* Returns the first number that is both common to nlp1, nlp2
* and > lowlimit.
* If no number > lowlimit is common, returns 0.
* normally lowlimit should be common to both lists, but this
* is not required.
*/
GLint NumpairGTIntersection(Numlist_p nlp1, Numlist_p nlp2, GLint low)
{
GLint i,j; /* indices into the lists of nlp1, nlp2 */
GLint l1,l2;
struct numpair *s1,*s2;
GLint change, ilo, ihi;
l1 = nlp1->len;
l2 = nlp2->len;
s1 = nlp1->list;
s2 = nlp2->list;
/* skip the nobrainers */
if (!l1 || !l2 || low > s1[l1-1].hi || low > s2[l2-1].hi) {
return 0;
}
/* find i nearest to low limit in list 1. this could be pivoted. */
for ( i = 0; i < l1; i++) {
if ( s1[i].hi > low) {
/* found first entry containing a number > low */
break;
}
}
/* find j nearest to low limit in list 2 */
for ( j = 0; j < l2; j++) {
if ( s2[j].hi > low) {
/* found first entry containing a number > low */
break;
}
}
change = 1;
/* now find first overlapping ij pair of ranges */
while (change) {
change = 0;
while ( j < l2 && i < l1 && s1[i].lo > s2[j].hi) {
j++;
change = 1;
}
while ( i < l1 && j < l2 && s2[j].lo > s1[i].hi) {
i++;
change = 1;
}
}
if (i == l1 || j == l2) {
/* no overlap */
return 0;
}
ilo = (s1[i].lo > s2[j].lo) ? s1[i].lo : s2[j].lo;
ihi = (s1[i].hi < s2[j].hi) ? s1[i].hi : s2[j].hi;
assert(ihi >= ilo);
if (ilo <= low) {
ilo = low + 1;
}
if (ilo > ihi) {
return 0;
}
return ilo;
}
GLint NumpairIntersectionLTHinted(Numlist_p nlp1, GLint *hint1,
Numlist_p nlp2, GLint *hint2,
GLint high)
{
GLint i,j; /* indices GLinto the lists of nlp1, nlp2 */
GLint l1,l2;
struct numpair *s1,*s2;
GLint change, ilo, ihi;
l1 = nlp1->len;
l2 = nlp2->len;
s1 = nlp1->list;
s2 = nlp2->list;
if (high <= 0) {
high = GL_INT_MAX;
}
/* skip the nobrainers */
if (!l1 || !l2 || high < s1[0].lo || high < s2[0].lo) {
return 0;
}
/* find i nearest to high limit in list 1. this could be pivoted. */
if (*hint1 >= 0) {
i = *hint1;
} else {
i = l1 - 1;
}
for ( ; i >= 0; i--) {
if ( s1[i].lo < high) {
/* found first entry containing a number < high */
break;
}
}
/* find j nearest to high limit in list 2 */
if (*hint2 >= 0) {
j = *hint2;
} else {
j = l2 - 1;
}
for ( ; j >= 0; j--) {
if ( s2[j].lo < high) {
/* found first entry containing a number < high */
break;
}
}
change = 1;
/* now find first overlapping ij pair of ranges */
while (change) {
change = 0;
while ( j >= 0 && i >= 0 && s1[i].hi < s2[j].lo) {
j--;
change = 1;
}
while ( i >= 0 && j >= 0 && s2[j].hi < s1[i].lo) {
i--;
change = 1;
}
}
if (i < 0 || j < 0) {
/* no overlap */
return 0;
}
/* compute overlap */
ilo = (s1[i].lo > s2[j].lo) ? s1[i].lo : s2[j].lo; /* max .lo */
ihi = (s1[i].hi < s2[j].hi) ? s1[i].hi : s2[j].hi; /* min .hi */
assert(ihi >= ilo);
if (ihi >= high) {
ihi = high - 1;
}
if (ihi < ilo) {
return 0;
}
*hint1 = i;
*hint2 = j;
return ihi;
}
GLint NumpairCardinality(Numlist_p nlp)
{
GLint i,len,count;
struct numpair *s;
if (nlp == NULL || nlp->len == 0 || nlp->list == NULL) {
return 0;
}
count = 0;
len = nlp->len;
s = nlp->list;
for (i = 0 ; i < len; i++) {
count += (s[i].hi - s[i].lo); /* to be complete, add 1 at each iteration */
}
/* since we didn't add 1 at each iteration, we can add len at the END
* to save len-1 additions.
*/
count += len;
return count;
}
void NumpairClearPuddle(void)
{
int len,i;
len = GRN.shared_len;
i = 0;
for (i = 0; i < len ;i++) {
NumpairDestroyList(GRN.shared[i].headlist);
GRN.shared[i].headlist = NULL;
}
GRN.shared_len = 0;
GRN.shared_cap = 0;
if (GRN.shared != NULL) {
ascfree(GRN.shared);
GRN.shared = NULL;
}
#if NUMLISTUSESPOOL
numlist_destroy_pool();
#endif
}
/*
* test the basic operations.
*/
#ifdef UNRELOCATE_NP_TEST
#ifdef NUMPAIRSELFTEST /* if defined, compile test main */
#define TESTITER 0
#define TESTLT 0
#define TESTGT 0
#define TESTINT 1
#ifdef REIMPLEMENT_STREAMS
FILE *g_ascend_errors = stderr;
#endif
int main()
{
Numlist_p p1,p2,p3,ep4,ep5,ep6,ep7,ep8,ep9, es1,es2, p10;
struct gl_list_t *nlpgl;
GLint last, hint, gt, h1, h2;
gl_init();
gl_init_pool();
p1 = NumpairElementary(7);
FPRINTF(stderr,"p1\n");
NLPWrite(NULL,p1);
p2 = NumpairDuplicate(p1,10);
FPRINTF(stderr,"p2\n");
NLPWrite(NULL,p2);
p3 = NumpairCopyList(p2);
FPRINTF(stderr,"p3\n");
NLPWrite(NULL,p3);
ep4 = NumpairExpandableList(NULL,3);
NumpairAppendList(ep4,5);
FPRINTF(stderr,"ep4\n");
NLPWrite(NULL,ep4);
NumpairAppendList(ep4,9);
FPRINTF(stderr,"ep4\n");
NLPWrite(NULL,ep4);
NumpairAppendList(ep4,6);
FPRINTF(stderr,"ep4\n");
NLPWrite(NULL,ep4);
NumpairAppendList(ep4,4);
FPRINTF(stderr,"ep4\n");
NLPWrite(NULL,ep4);
NumpairAppendList(ep4,10);
FPRINTF(stderr,"ep4\n");
NLPWrite(NULL,ep4);
NumpairAppendList(ep4,14);
FPRINTF(stderr,"ep4\n");
NLPWrite(NULL,ep4);
NumpairAppendList(ep4,17);
FPRINTF(stderr,"ep4\n");
NLPWrite(NULL,ep4);
NumpairAppendList(ep4,23);
FPRINTF(stderr,"ep4\n");
NLPWrite(NULL,ep4);
ep5 = NumpairExpandableList(NULL,8);
FPRINTF(stderr,"ep5\n");
NLPWrite(NULL,ep5);
NumpairMergeLists(p1,ep4,ep5);
FPRINTF(stderr,"ep5\n");
NLPWrite(NULL,ep5);
#if TESTITER
/* test iteration */
FPRINTF(stderr,"\nIteration tests\n");
FPRINTF(stderr,"Prev\n");
hint = 0;
last = 0;
while (hint != -1) {
last = NumpairPrevNumber(ep5,last,&hint);
FPRINTF(stderr,"prev - %d hint %d\n", last , hint );
}
FPRINTF(stderr,"Next\n");
hint = 0;
last = 0;
while (hint != -1) {
last = NumpairNextNumber(ep5,last,&hint);
FPRINTF(stderr,"next - %d hint %d\n", last , hint );
}
#endif
ep6 = NumpairExpandableList(NULL,1);
NumpairAppendList(ep6,18);
NumpairAppendList(ep6,29);
NumpairAppendList(ep6,44);
ep7 = NumpairExpandableList(NULL,1);
NumpairAppendList(ep7,28);
NumpairAppendList(ep7,15);
NumpairAppendList(ep7,3);
ep8 = NumpairExpandableList(NULL,1);
NumpairAppendList(ep8,27);
NumpairAppendList(ep8,13);
NumpairAppendList(ep8,1);
ep9 = NumpairExpandableList(NULL,1);
NumpairAppendList(ep9,72);
NumpairAppendList(ep9,31);
NumpairAppendList(ep9,24);
nlpgl = gl_create(10);
gl_append_ptr(nlpgl,ep4);
gl_append_ptr(nlpgl,ep5);
gl_append_ptr(nlpgl,ep6);
gl_append_ptr(nlpgl,ep7);
gl_append_ptr(nlpgl,ep8);
gl_append_ptr(nlpgl,ep9);
es1 = NumpairExpandableList(NULL,5);
es2 = NumpairExpandableList(NULL,5);
FPRINTF(stderr,"\n MERGE TESTS:\n");
FPRINTF(stderr,"\nep4\n");
NLPWrite(NULL,ep4);
FPRINTF(stderr,"\nep5\n");
NLPWrite(NULL,ep5);
FPRINTF(stderr,"\nep6\n");
NLPWrite(NULL,ep6);
FPRINTF(stderr,"\nep7\n");
NLPWrite(NULL,ep7);
FPRINTF(stderr,"\nep8\n");
NLPWrite(NULL,ep8);
FPRINTF(stderr,"\nep9\n");
NLPWrite(NULL,ep9);
p10 = NumpairCombineLists(nlpgl,es1,es2);
FPRINTF(stderr,"\nes1\n");
NLPWrite(NULL,es1);
FPRINTF(stderr,"\nes2\n");
NLPWrite(NULL,es2);
FPRINTF(stderr,"\np10\n");
NLPWrite(NULL,p10);
#if TESTLT
FPRINTF(stderr,"LTIntersection Test ep5, es2\n");
for (last = 46; last > 0; last--) {
h1 = h2 = -1;
gt = NumpairIntersectionLTHinted(ep5,&h1,es2,&h2,last);
FPRINTF(stderr,"highlimit = %d, result = %d\n",last,gt);
}
FPRINTF(stderr,"LTIntersection Test ep5, es2\n");
h1 = h2 = -1;
for (last = 0; last > 0 || h1 < 0; ) {
gt = NumpairIntersectionLTHinted(ep5,&h1,es2,&h2,last);
FPRINTF(stderr,"highlimit = %d, result = %d\n",last,gt);
FPRINTF(stderr," h1 = %d, h2 = %d\n",h1,h2);
last = gt;
}
#endif
#if TESTGT
FPRINTF(stderr,"GTIntersection Test ep5, es2\n");
for (last = 0; last < 46; last++) {
gt = NumpairGTIntersection(ep5,es2,last);
FPRINTF(stderr,"lowlimit = %d, result = %d\n",last,gt);
}
FPRINTF(stderr,"GTIntersection Test es2, ep5\n");
for (last = 0; last < 46; last++) {
gt = NumpairGTIntersection(es2,ep5,last);
FPRINTF(stderr,"lowlimit = %d, result = %d\n",last,gt);
}
FPRINTF(stderr,"GTIntersection Test ep5, ep5\n");
for (last = 0; last < 26; last++) {
gt = NumpairGTIntersection(ep5,ep5,last);
FPRINTF(stderr,"lowlimit = %d, result = %d\n",last,gt);
}
#endif
FPRINTF(stderr,"\nCalcIntersection Test ep8+29, es1\n");
NumpairAppendList(ep8,29);
FPRINTF(stderr,"ep8\n");
NLPWrite(NULL,ep8);
FPRINTF(stderr,"\nes1\n");
NLPWrite(NULL,es1);
NumpairClearList(ep5);
NumpairCalcIntersection(ep8,es1,ep5);
FPRINTF(stderr,"\nresult ep5:\n");
NLPWrite(NULL,ep5);
FPRINTF(stderr,"\nCalcIntersection Test es1, ep8+29\n");
NumpairClearList(ep5);
NumpairCalcIntersection(es1,ep8,ep5);
FPRINTF(stderr,"\nresult ep5:\n");
NLPWrite(NULL,ep5);
NumpairClearList(ep4);
NumpairClearList(ep5);
NumpairClearList(ep6);
for (h1 = 6; h1 <37; h1++) {
NumpairAppendList(ep4,h1);
}
NumpairAppendList(ep4,45);
NumpairAppendList(ep4,81);
NumpairAppendList(ep4,82);
for (h1 = 84; h1 <104; h1++) {
NumpairAppendList(ep4,h1);
}
NumpairAppendList(ep5,34);
NumpairAppendList(ep5,35);
NumpairAppendList(ep5,52);
NumpairAppendList(ep5,71);
NumpairAppendList(ep5,97);
NumpairAppendList(ep5,115);
FPRINTF(stderr,"\nep4:\n");
NLPWrite(NULL,ep4);
FPRINTF(stderr,"\nep5:\n");
NLPWrite(NULL,ep5);
NumpairCalcIntersection(ep4,ep5,ep6);
FPRINTF(stderr,"\nresult ep4 ^ ep5:\n");
NLPWrite(NULL,ep6);
NumpairCalcIntersection(ep5,ep4,ep6);
FPRINTF(stderr,"\nresult ep5 ^ ep4:\n");
NLPWrite(NULL,ep6);
NumpairDestroyList(p1);
NumpairDestroyList(p2);
NumpairDestroyList(p3);
NumpairDestroyList(ep4);
NumpairDestroyList(ep5);
NumpairDestroyList(ep6);
NumpairDestroyList(ep7);
NumpairDestroyList(ep8);
NumpairDestroyList(ep9);
NumpairDestroyList(p10);
NumpairDestroyList(es1);
NumpairDestroyList(es2);
NumpairClearPuddle();
gl_destroy(nlpgl);
gl_emptyrecycler(); /* empty the reused list pool */
gl_destroy_pool(); /* empty the reused list head pool */
exit(0);
}
#endif /* numpairselftest */
#endif /* unrelocatenptest */