/*  File: arraysub.c
 *  Author: Jean Thierry-Mieg (mieg@mrc-lmba.cam.ac.uk)
 *  Copyright (C) J Thierry-Mieg and R Durbin, 1991
 * -------------------------------------------------------------------
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * -------------------------------------------------------------------
 * This file is part of the ACEDB genome database package, written by
 * 	Richard Durbin (MRC LMB, UK) rd@mrc-lmba.cam.ac.uk, and
 *	Jean Thierry-Mieg (CRBM du CNRS, France) mieg@crbm.cnrs-mop.fr
 *
 * Description:
 *              Arbitrary length arrays, stacks, associators
 *              line breaking and all sorts of other goodies
 *              These functions are declared in array.h
 *               (part of regular.h - the header for libfree.a)
 * Exported functions:
 *              See Header file: array.h (includes lots of macros)
 * HISTORY:
 * Last edited: Mar 14 13:53 2003 (rnc)
 * * May 19 13:51 1999 (fw): make MEM_DEBUG the default for all
 *              ops that demand memory, so we can track where the
 *              code made that request.
 * * Nov  1 16:11 1996 (srk)
 *		-	MEM_DEBUG code clean-up 
 *                      (some loose ends crept in from WIN32)
 *		-	int (*order)(void*,void*) prototypes used 
 *                                            uniformly throughout
 * * Jun  5 00:48 1996 (rd)
 * * May  2 22:33 1995 (mieg): killed the arrayReport at 20000
      otherwise it swamps 50 Mbytes of RAM
 * * Jan 21 16:25 1992 (mieg): messcrash in uArrayCreate(size<=0)
 * * Dec 17 11:40 1991 (mieg): stackTokeniseTextOn() tokeniser.
 * * Dec 12 15:45 1991 (mieg): Stack magic and stackNextText
 * Created: Thu Dec 12 15:43:25 1989 (mieg)
 *-------------------------------------------------------------------
 */

/* $Id: arraysub.c,v 1.70 2003/07/22 02:57:30 mieg Exp $	 */

   /* Warning : if you modify Array or Stack structures or 
      procedures in this file or array.h, you may need to modify 
      accordingly the persistent array package asubs.c.
   */

#include "regular.h"
#include <wh/bitset.h>

extern BOOL finalCleanup ;	/* in memsubs.c */

/********** Array : class to implement variable length arrays **********/

static int totalAllocatedMemory = 0 ;
static int totalNumberCreated = 0 ;
static int totalNumberActive = 0 ;
static Array reportArray = 0 ;
static void uArrayFinalise (void *cp) ;

Array uArrayCreate_dbg (int n, int size, STORE_HANDLE handle,
			const char *hfname,int hlineno) 
{
  int id = totalNumberCreated++ ;
  Array new = (Array) handleAlloc_dbg (uArrayFinalise, 
				       handle,
				       sizeof (struct ArrayStruct),
				       hfname, hlineno) ;

  if (!reportArray)
    { reportArray = (Array)1 ; /* prevents looping */
      reportArray = arrayCreate (512, Array) ;
    }
  if (size <= 0)
    messcrash("negative size %d in uArrayCreate", size) ;
  if (n < 1)
    n = 1 ;
  totalAllocatedMemory += n * size ;

  /* base-mem isn't allocated upon handle, it's free'd by finalisation */
  new->base = halloc_dbg (n*size, 0, hfname, hlineno) ;
  new->dim = n ;
  new->max = 0 ;
  new->size = size ;
  new->id = ++id ;
  new->magic = ARRAY_MAGIC ;
  totalNumberActive++ ;
  if (reportArray != (Array)1) 
    { if (new->id < 20000)
	array (reportArray, new->id, Array) = new ;
      else
	{ Array aa = reportArray ;
	  reportArray = (Array)1 ; /* prevents looping */
	  arrayDestroy (aa) ;
	}
    }
  return new ;
}

/**************/

int arrayReportMark (void)
{
  return reportArray != (Array)1 ?  
      arrayMax(reportArray) : 0 ;
}

/**************/

void arrayReport (int j)
{ int i ;
  Array a ;

 if (reportArray == (Array)1) 
   { fprintf(stderr,
	     "\n\n %d active arrays, %d created, %d kb allocated\n\n ",   
	     totalNumberActive,   totalNumberCreated , totalAllocatedMemory/1024) ;
     return ;
   }
  
  fprintf(stderr,"\n\n") ;
  
  i = arrayMax (reportArray) ;
  while (i-- && i > j)
    { a = arr (reportArray, i, Array) ;
      if (arrayExists(a))
	fprintf (stderr, "Array %d  size=%d max=%d\n", i, a->size, a->max) ;
    }
}

/**************/

void arrayStatus (int *nmadep, int *nusedp, int *memAllocp, int *memUsedp)
{ 
  int i ;
  Array a, *ap ;

  *nmadep = totalNumberCreated ; 
  *nusedp = totalNumberActive ;
  *memAllocp = totalAllocatedMemory ;
  *memUsedp = 0 ;

  if (reportArray == (Array)1) 
    return ;

  i = arrayMax(reportArray) ;
  ap = arrp(reportArray, 0, Array) - 1 ;
  while (ap++, i--)
    if (arrayExists (*ap))
      { a = *ap ;
	*memUsedp += a->max * a->size ;
      }
}

/**************/

Array uArrayReCreate_dbg (Array a, int n, int size,
			  const char *hfname, int hlineno)
{
  if (!arrayExists(a))
    return  uArrayCreate_dbg(n, size, 0, hfname, hlineno) ;

  if(a->size != size)
    uMessSetErrorOrigin(hfname, hlineno),
      uMessCrash ("Type missmatch in arrayReCreate, you should always "
		  "call recreate using the same type") ;

  if (n < 1)
    n = 1 ;
  if (a->dim < n || 
      (a->dim - n)*size > (1 << 19) ) /* free if save > 1/2 meg */
    { totalAllocatedMemory -= a->dim * size ;
      messfree (a->base) ;
      a->dim = n ;
      totalAllocatedMemory += a->dim * size ;
      /* base-mem isn't alloc'd on handle, it's free'd by finalisation */
      a->base = halloc_dbg (a->dim*size, 0, hfname, hlineno) ;
    }
  memset(a->base,0,(mysize_t)(a->dim*size)) ;

  a->max = 0 ;
  return a ;
}

/**************/

void uArrayDestroy_dbg (Array a,
			const char *hfname, int hlineno)
/* Note that the finalisation code attached to the memory does the work, 
   see below */
{
  if (!a) return;

  if (a->magic != ARRAY_MAGIC)
    uMessSetErrorOrigin(hfname, hlineno),
      uMessCrash ("arrayDestroy received corrupt array->magic");

  messfree(a);
}

static void uArrayFinalise (void *cp)
{ Array a = (Array)cp;

  totalAllocatedMemory -= a->dim * a->size ;
  if (!finalCleanup) messfree (a->base) ;
  a->magic = 0 ;
  totalNumberActive-- ;
  if (!finalCleanup && reportArray != (Array)1) 
    arr(reportArray, a->id, Array) = 0 ;

  return;
} /* uArrayFinalise */

/******************************/

void arrayExtend_dbg (Array a, int n, const char *hfname,int hlineno) 
{
  char *new ;

  if (!a || n < a->dim)
    return ;

  totalAllocatedMemory -= a->dim * a->size ;
  if (a->dim*a->size < 1 << 23)  /* 2 megs of keys, or 8 megs of ram */
    a->dim *= 2 ;
  else
    a->dim += 1024 + ((1 << 23) / a->size) ;
  if (n >= a->dim)
    a->dim = n + 1 ;

  totalAllocatedMemory += a->dim * a->size ;

  /* base-mem isn't allocated upon handle, it's free'd by finalisation */
  new = halloc_dbg (a->dim*a->size, 0, hfname, hlineno) ;
  memcpy (new,a->base,a->size*a->max) ;
  messfree (a->base) ;
  a->base = new ;

  return;
} /* arrayExtend_dbg */

/***************/

char *uArray_dbg (Array a, int i, const char *hfname, int hlineno)
{
  if (i < 0)
    uMessSetErrorOrigin(hfname, hlineno),
      uMessCrash ("referencing array element %d < 0", i) ;

  if (!a)
    uMessSetErrorOrigin(hfname, hlineno),
      uMessCrash ("uArray called with NULL Array struc");

  if (i >= a->max)
    { if (i >= a->dim)
        arrayExtend (a,i) ;
      a->max = i+1 ;
    }
  return a->base + i*a->size ;
}

/***************/

char *uArrayCheck_dbg (Array a, int i, const char *hfname,int hlineno)
{
  if (i < 0)
    uMessSetErrorOrigin(hfname, hlineno),
      uMessCrash ("referencing array element %d < 0", i) ;

  return uArray_dbg(a, i, hfname, hlineno) ;
}

/***************/

char *uArrCheck_dbg (Array a, int i, const char *hfname, int hlineno)
{
  if (i >= a->max || i < 0)
    uMessSetErrorOrigin(hfname, hlineno),
      uMessCrash ("array index %d out of bounds [0,%d]",
		  i, a->max - 1) ;

  return a->base + i*a->size ;
}

/**************/

Array arrayCopy_dbg(Array old, STORE_HANDLE handle,
		    const char *hfname, int hlineno) 
{
  Array new ;
  
  if (arrayExists (old) && old->size)
    {
      new = uArrayCreate_dbg (old->dim, old->size, handle,
			      hfname, hlineno) ;
      memcpy(new->base, old->base, old->dim * old->size);
      new->max = old->max ;
      return new;
    }

  return (Array)0 ;
} /* arrayCopy_dbg */

/**************/

Array arrayTruncatedCopy_dbg (Array a, int x1, int x2,
			      const char *hfname, int hlineno)
{ Array b = 0 ;
  
  if (x1 < 0 || x2 < x1 || x2 > a->max)
    messcrash 
      ("Bad coordinates x1 = %d, x2 = %d in arrayTruncatedCopy",
       x1, x2) ;
  if (arrayExists (a) && a->size)
    { if (x2 - x1)
	{ b = uArrayCreate_dbg (x2 - x1, a->size, 0, hfname, hlineno) ;
	  b->max = x2 - x1 ;
	  memcpy(b->base, a->base + x1, b->max * b->size);
	}
      else
	b = uArrayCreate_dbg (10, a->size, 0, hfname, hlineno) ;
    }
  return b;
}

/**************/

void arrayCompress(Array a)
{
  int i, j, k , as ;
  char *x, *y, *ab  ;
  
  if (!a || !a->size || arrayMax(a) < 2 )
    return ;

  ab = a->base ; 
  as = a->size ;
  for (i = 1, j = 0 ; i < arrayMax(a) ; i++)
    { x = ab + i * as ; y = ab + j * as ;
      for (k = a->size ; k-- ;)		
	if (*x++ != *y++) 
	  goto different ;
      continue ;
      
    different:
      if (i != ++j)
	{ x = ab + i * as ; y = ab + j * as ;
	  for (k = a->size ; k-- ;)	 
	    *y++ = *x++ ;
	}
    }
  arrayMax(a) = j + 1 ;
}

/****************/

/* 31.7.1995 dok408  added arraySortPos() - restricted sorting to tail of array */
void arraySort(Array a, int (*order)(void*, void*))
{
  arraySortPos(a, 0, order) ;

  return ;
}

void arraySortPos (Array a, int pos, int (*order)(void*, void*))
{
  typedef int (*CONSTORDER)(const void*, const void*) ;
  unsigned int n = a->max - pos, s = a->size ;
  void *v = a->base + pos * a->size ;
 
  if (pos < 0)
    messcrash("arraySortPos: pos = %d", pos);

  if (n > 1) 
    {
#ifdef SOLARIS
      /* Jean reported that qsort() crashes on solaris 5.7 */
      mSort (v, n, s, order) ;
#else
      qsort (v, n, s, (CONSTORDER)order) ;
#endif
    }

  return ;
}

/***********************************************************/

BOOL arrayIsEntry_dbg (Array a, int i, void *s,
		       const char *hfname, int hlineno)
{
  char *cp = uArray_dbg(a,i, hfname, hlineno), *cq = s ;
  int j = a->size;

  while (j--)
    if (*cp++ != *cq++) 
      return FALSE ;
  return TRUE;
}

/***********************************************/
       /* Finds Entry s from Array  a
        * sorted in ascending order of order()
        * If found, returns TRUE and sets *ip
        * if not, returns FALSE and sets *ip one step left
        */

BOOL arrayFind(Array a, void *s, int *ip, int (* order)(void*, void*))
{
  int ord;
  int i = 0 , j = arrayMax(a), k;

  if(!j || (ord = order(s,uArray_dbg(a,0, __FILE__, __LINE__)))<0)
    { if (ip)
	*ip = -1; 
      return FALSE;
    }   /* not found */

  if (ord == 0)
    { if (ip)
	*ip = 0;
      return TRUE;
    }

  if ((ord = order(s,uArray_dbg(a,--j, __FILE__, __LINE__)))>0 )
    { if (ip)
	*ip = j; 
      return FALSE;
    }
  
  if (ord == 0)
    { if (ip)
	*ip = j;
      return TRUE;
    }

  while(TRUE)
    { k = i + ((j-i) >> 1) ; /* midpoint */
      if ((ord = order(s, uArray_dbg(a,k, __FILE__, __LINE__))) == 0)
	{ if (ip)
	    *ip = k; 
	  return TRUE;
	}
      if (ord > 0) 
	(i = k);
      else
	(j = k) ;
      if (i == (j-1) )
        break;
    }
  if (ip)
    *ip = i ;
  return FALSE;
}

/**************************************************************/
       /* Removes Entry s from Array  a
        * sorted in ascending order of order()
        */

BOOL arrayRemove (Array a, void * s, int (* order)(void*, void*))
{
  int i;

  if (arrayFind(a, s, &i,order))
    {
      /* memcpy would be faster but regions overlap
       * and memcpy is said to fail with some compilers
       */
      char *cp = uArray_dbg(a,i, __FILE__, __LINE__),  *cq = cp + a->size ;
      int j = (arrayMax(a) - i)*(a->size) ;
      while(j--)
	*cp++ = *cq++;

      arrayMax(a) --;
      return TRUE;
    }
  else

    return FALSE;
}

/**************************************************************/
       /* Insert Segment s in Array  a
        * in ascending order of s.begin
        */

BOOL arrayInsert(Array a, void * s, int (*order)(void*, void*))
{
  int i, j, arraySize;

  if (arrayFind(a, s, &i,order))
    return FALSE;  /* no doubles */
  
  arraySize = arrayMax(a);
  j = arraySize + 1;

  uArray_dbg(a,j-1,__FILE__,__LINE__) ; /* to create space */

	/* avoid memcpy for same reasons as above */
  {
    char *cp, *cq;
    int k;
    
    if (arraySize > 0)
      { 
	cp = uArray_dbg(a,j - 1,__FILE__,__LINE__) + a->size - 1;
	cq = cp - a->size ;
	k = (j - i - 1)*(a->size);
	while(k--)
	  *cp-- = *cq--;
      }
    
    cp = uArray_dbg(a,i+1,__FILE__,__LINE__); 
    cq = (char *) s; 
    k = a->size;
    while(k--)
      *cp++ = *cq++;
  }
  return TRUE;
}

/********* BitSet - inherits from Array **********/
/* mieg 2001, turn these into function to systematically make room
   allowing to use bit() without errors
   which is ok since anyway bitSetMax is not a defined method
*/
Array bitSetCreate (int n, STORE_HANDLE h)
{
  Array bb  = 0 ;
  if (n < 256) n = 256 ;
  bb = arrayHandleCreate (1 + (n >> 5), unsigned int, h) ;
  array (bb, (n >> 5), unsigned int) = 0 ;
  return bb ;
}

Array bitSetReCreate (Array bb, int n) 
{
  if (n < 256) n = 256 ;
  bb = arrayReCreate (bb, 1 + (n >> 5), unsigned int) ;
  array (bb, (n >> 5), unsigned int) = 0 ;
  return bb ;
}

void bitExtend (Array bb, int n)
{
  if (n < 256) n = 256 ;
  arrayExtend(bb, n >> 5) ;
  array (bb, (n >> 5), unsigned int) = 0 ;
}

                  /* Returns the number of set bits */
int bitSetCount (BitSet a)
{
  register unsigned int *cp ;
  register unsigned int i, i1;
  register int j = arrayExists(a) ? arrayMax(a) : 0 , n = 0 ;

  if(!j)
    return 0 ;
  cp  = arrp(a,0,unsigned int) ;
  while (j--)
    { if(!*cp) n+=32 ;
      else if(*cp != 0xff)
	for (i=1, i1 = 0 ; i1 < 32 ; i1++, i <<= 1) if(~(*cp) & i) n++;
      cp++;
    }
  return 32*arrayMax(a) - n ;
}

/******** Stack : arbitrary Stack class - inherits from Array *********/

static void uStackFinalise (void *cp) ;

Stack stackHandleCreate_dbg (int n,  /* n is initial size */
			     STORE_HANDLE handle, 
			     const char *hfname, int hlineno)
{
  Stack s = (Stack) handleAlloc_dbg (uStackFinalise, 
				     handle,
				     sizeof (struct StackStruct),
				     hfname, hlineno) ;
  s->magic = STACK_MAGIC ;
  /* array is not allocated upon handle, it's free'd by finalisation */
  s->a = arrayCreate (n,char) ;

  s->pos = s->ptr = s->a->base ;
  s->safe = s->a->base + s->a->dim - 16 ; /* safe to store objs up to size 8 */
  s->textOnly = FALSE;

  return s ;
} /* stackHandleCreate_dbg */

void stackTextOnly(Stack s)
{ if (!stackExists(s))
    messcrash("StackTextOnly given non-exisant stack.");

  s->textOnly = TRUE;
}

Stack stackReCreate_dbg (Stack s, int n,  /* n is initial size */
			 const char *hfname, int hlineno)
{
  if (!stackExists(s))
    return stackHandleCreate_dbg(n, 0, hfname, hlineno) ;

  /* array is not allocated upon handle, it's free'd by finalisation */
  s->a = uArrayReCreate_dbg (s->a,n,sizeof(char), hfname, hlineno) ;

  s->pos = s->ptr = s->a->base ;
  s->safe = s->a->base + s->a->dim - 16 ; /* safe to store objs up to size 8 */

  return s ;
} /* stackReCreate_dbg */

Stack stackCopy_dbg (Stack old, STORE_HANDLE handle,
		     const char *hfname, int hlineno)
{
  Stack new ;
  int ptr, pos;

  if (!stackExists(old))
    return 0 ;

  ptr = old->ptr - old->a->base;
  pos = old->pos - old->a->base;

  new = (Stack) handleAlloc_dbg (uStackFinalise, handle,
                                 sizeof (struct StackStruct),
                                 hfname, hlineno) ;
  new->magic = STACK_MAGIC;

  /* array is not allocated upon handle, it's free'd by finalisation */
  new->a = arrayCopy_dbg (old->a, 0, hfname, hlineno) ;

  new->ptr = new->a->base + ptr;
  new->pos = new->a->base + pos;
  new->safe = new->a->base + new->a->dim - 16 ;
  new->textOnly = old->textOnly;

  return new ;
} /* stackCopy_dbg */

#ifdef FW_990519_NOT_DECLARED_OR_USED_ANYWHERE
Stack arrayToStack (Array a)
{ 
  Stack s ;
  int n ;

  if (!arrayExists(a) || a->size != 1 )
    return 0 ;
    
  n = arrayMax(a) ;
  s = stackCreate(n  + 32) ;
              
  memcpy(s->a->base, a->base, n) ;
                
  s->pos = s->a->base ;
  s->ptr = s->a->base + n ;
  s->safe = s->a->base + s->a->dim - 16 ; /* safe to store objs up to size 8 */
  while ((long)s->ptr % STACK_ALIGNMENT)
    *(s->ptr)++ = 0 ;   
  return s ;
}
#endif /* FW_990519_NOT_DEFINE_OR_USED_ANYWHERE */

void uStackDestroy(Stack s)
{ if (s && s->magic == STACK_MAGIC) messfree(s);
} /* the rest is done below as a consequence */

static void uStackFinalise (void *cp)
{ Stack s = (Stack)cp;
  if (!finalCleanup) arrayDestroy (s->a) ;
  s->magic = 0 ;
}

void stackExtend_dbg (Stack s, int n,
		      const char *hfname, int hlineno)
{
  int ptr, pos;

  if (!stackExists(s))
    uMessSetErrorOrigin (hfname, hlineno), uMessCrash("stackExtend() - called with invalid stack");

  ptr = s->ptr - s->a->base;
  pos = s->pos - s->a->base ;

  s->a->max = s->a->dim ;	/* since only up to ->max copied over */
  arrayExtend_dbg (s->a,ptr+n+16, hfname, hlineno);	/* relies on arrayExtend mechanism */
  s->ptr = s->a->base + ptr ;
  s->pos = s->a->base + pos ;
  s->safe = s->a->base + s->a->dim - 16 ;

  return;
} /* stackExtend_dbg */

int stackMark (Stack s)
{
  int mark;

  if (!stackExists(s))
    messcrash("stackMark() - received invalid stack");

  mark = (s->ptr - s->a->base);
  
  return mark;
} /* stackMark */

int stackPos (Stack s)
{ 
  int pos;

  if (!stackExists(s))
    messcrash("stackPos() - received invalid stack");
  
  pos = (s->pos - s->a->base);

  return pos;
} /* stackPos */

void stackCursor (Stack s, int mark)
{ 
  if (!stackExists(s))
    messcrash("stackCursor() - received invalid stack");

  s->pos = s->a->base + mark ;

  return;
} /* stackCursor */

char *stackText_dbg (Stack s, int mark, const char *hfname, int hlineno)
{
  char *text;

  if (!stackExists(s))
    uMessSetErrorOrigin (hfname, hlineno), uMessCrash("stackText() - called with invalid stack");

  text = (char*)(s->a->base + mark);

  return text;
} /* stackText */

/************************************************************/

/* access doubles on the stack by steam on systems where we don't
   align the stack strictly enough, this code assumes that ints are 32bits
   and doubles 64 */

union mangle { double d;
	       struct { int i1;
			int i2;
		      } i;
	     };

void uStackDoublePush(Stack stk, double x)
{ union mangle m;

  m.d = x;
  push(stk, m.i.i1, int);
  push(stk, m.i.i2, int);
}

double uStackDoublePop(Stack stk)
{ union mangle m;

  m.i.i2 = pop(stk, int);
  m.i.i1 = pop(stk, int);

  return m.d;
}

double uStackDoubleNext(Stack stk)
{ union mangle m;

  m.i.i1 = stackNext(stk, int);
  m.i.i2 = stackNext(stk, int);

  return m.d;
}

void pushText (Stack s, char* text)
{
  while (s->ptr + strlen(text)  > s->safe) 
    stackExtend (s,strlen(text)+1) ;
  while ((*(s->ptr)++ = *text++)) ;
  if (!s->textOnly)
    while ((long)s->ptr % STACK_ALIGNMENT)
      *(s->ptr)++ = 0 ;   
}

char* popText (Stack s)
{
  char *base = s->a->base ;

  while (s->ptr > base && !*--(s->ptr)) ;
  while (s->ptr >= base && *--(s->ptr)) ;
  return ++(s->ptr) ;
}

void catText (Stack s, char* text)
{
  while (s->ptr + strlen(text) > s->safe)
    stackExtend (s,strlen(text)+1) ;
  *s->ptr = 0 ;
  while (s->ptr >= s->a->base && *s->ptr == 0)
    s->ptr -- ;
  s->ptr ++ ;
  while ((*(s->ptr)++ = *text++)) ;
  if (!s->textOnly)
    while ((long)s->ptr % STACK_ALIGNMENT)
      *(s->ptr)++ = 0 ;   
}

void catBinary (Stack s, char* data, int size)
{
  int total;
  total = size + 1;

  while (s->ptr + total > s->safe)
    stackExtend (s,size+1) ;
  memcpy(s->ptr,data,size);
  s->ptr += size;
}

char* stackNextText (Stack s)
{ char *text = s->pos ;
  if (text>= s->ptr)
    return 0 ;  /* JTM, so while stackNextText makes sense */
  while (*s->pos++) ;
  if (!s->textOnly)
    while ((long)s->pos % STACK_ALIGNMENT)
      ++s->pos ;
  return text ;
}

/*********/
     /* Push text in stack s, after breaking it on delimiters */
     /* You can later access the tokens with command
	while (token = stackNextText(s)) work on your tokens ;
	*/
void  stackTokeniseTextOn(Stack s, char *text, char *delimiters)
{
  char *cp, *cq , *cend, *cd, old, oldend ;
  int i, n ;

  if(!stackExists(s) || !text || !delimiters)
    messcrash("stackTextOn received some null parameter") ;

  n = strlen(delimiters) ;
  cp = cq  = text ;
  while(TRUE)
    {
      while(*cp == ' ')
	cp++ ;
      cq = cp ;
      old = 0 ;
      while(*cq)
	{ for (cd = delimiters, i = 0 ; i < n ; cd++, i++)
	    if (*cd == *cq)
	      { old = *cq ;
		*cq = 0 ;
		goto found ;
	      }
	  cq++ ;
	}
    found:
      cend = cq ;
      while(cend > cp && *--cend == ' ') ;
      if (*cend != ' ') cend++ ;
      oldend = *cend ; *cend = 0 ;
      if (*cp && cend > cp)
	pushText(s,cp) ;
      *cend = oldend ;
      if(!old)
	{ stackCursor(s, 0) ;
	  return ;
	}
      *cq = old ;
      cp = cq + 1 ;
    }
}

void stackClear (Stack s)
{ 
  if (!s) messcrash("stackClear() - NULL stack");
  if (!stackExists(s)) messcrash("stackClear() - non-magic Stack");

  memset(s->a->base,0,(mysize_t)(s->a->dim*sizeof(char))) ;

  s->pos = s->ptr = s->a->base;
  s->a->max = 0;

  return;
} /* stackClear */

/****************** routines to set text into lines ***********************/

static char *linesText ;
static Array lines ;
static Array textcopy ;
static int kLine, popLine ;

/**********/

int uLinesText (char *text, int width)
{
  char *cp,*bp ;
  int i ;
  int nlines = 0 ;
  int length = strlen (text) ;
  int safe = length + 2*(length/(width > 0 ? width : 1) + 1) ; /* mieg: avoid zero divide */
  static int isFirst = TRUE ;

  if (isFirst)
    { isFirst = FALSE ;
      lines = arrayCreate(16,char*) ;
      textcopy = arrayCreate(128,char) ;
    }

  linesText = text ;
  array(textcopy,safe,char) = 0 ;   /* ensures textcopy is long enough */

  if (!*text)
    { nlines = popLine = kLine = 0 ;
      array(lines,0,char*) = 0 ;
      return 0 ;
    }

  cp = textcopy->base ;
  nlines = 0 ;
  for (bp = text ; *bp ; ++bp)
    { array(lines,nlines++,char*) = cp ;
      for (i = 0 ; (*cp = *bp) && *cp != '\n' ; ++i, ++cp, ++bp)
        if (i == width)		/* back up to last space */
          { while (i--)
	      { --bp ; --cp ;
		if (*cp == ' ' || *cp == ',' || *cp == ';')
		  goto eol ;
	      }
	    cp += width ;	/* no coma or spaces in whole line ! */
	    bp += width ;
	    break ;
	  }
eol:  if (!*cp)
        break ;
      if (*cp != '\n')
	++cp ;
      *cp++ = 0 ;
    }
  kLine = 0 ;			/* reset for uNextLine() */
  popLine = nlines ;
  array(lines,nlines,char*) = 0 ; /* 0 terminate */

  return nlines ;
 }

char *uNextLine (char *text)
 {
   if (text != linesText)
     messout ("Warning : uNextLine being called with bad context") ;
   return array(lines,kLine++,char*) ;
 }

char *uPopLine (char *text)
 {
   if (text != linesText)
     messout ("Warning : uPopLine being called with bad context") ;
   if (popLine)
     return array(lines,--popLine,char*) ;
   else return 0;
 }

char **uBrokenLines (char *text, int width)
{
  uLinesText (text,width) ;
  return (char**)lines->base ;
}

char *uBrokenText (char *text, int width)
{
  char *cp ;

  uLinesText (text,width) ;
  uNextLine (text) ;
  while ((cp = uNextLine (text)))
    *(cp-1) = '\n' ;
  return arrp(textcopy,0,char) ;
}

/*************************************************************/
/* perfmeters */
int assBounce = 0, assFound = 0, assNotFound = 0, assInserted = 0, assRemoved = 0 ;

/* Associator package is for associating pairs of pointers. 
   Assumes that an "in" item is non-zero and non -1.
   Implemented as a hash table of size 2^m.  
   
   Originally grabbed from Steve Om's sather code by Richard Durbin.

   Entirelly rewritten by mieg, using bouncing by relative primes
   and deletion flagging.

   User has access to structure member ->n = # of pairs
*/

#define VSIZE (sizeof(void*))
#define VS5 (VSIZE/5)
#define VS7 (VSIZE/7)
#define moins_un ((void*) (-1))

#define HASH(_xin) { register long z = VS5, x = (char*)(_xin) - (char*)0 ; \
		  for (hash = x, x >>= 5 ; z-- ; x >>= 5) hash ^= x ; \
		  hash &= a->mask ; \
		}

#define DELTA(_xin)   { register long z = VS7, x = (char*)(_xin) - (char*)0 ; \
		       for (delta = x, x >>= 7 ; z-- ; x >>= 7) delta ^= x ; \
		       delta = (delta & a->mask) | 0x01 ; \
		   }  /* delta odd is prime relative to  2^m */

/**************** Destruction ****************************/

static void assFinalise(void *cp)
{ Associator a = (Associator)cp;

  a->magic = 0 ;
  if (!finalCleanup)
    { messfree(a->in) ;
      messfree(a->out);
    }
}

void uAssDestroy (Associator a)
{ if (assExists(a))
    messfree (a) ;  /* calls assFinalise */
}

/**************** Creation *******************************/

static Associator assDoCreate_dbg (int nbits, STORE_HANDLE handle,
					const char *hfname, int hlineno)
{ static int nAss = 0 ;
  Associator a ;
  int size = 1 << nbits,  /* size must be a power of 2 */
      vsize = size * VSIZE ; 
  a = (Associator) handleAlloc_dbg(assFinalise, 
				   handle, 
				   sizeof (struct AssStruct),
				   hfname, hlineno) ;
  a->in = (void**) halloc_dbg(vsize, 0, hfname, hlineno) ;
  a->out = (void**) halloc_dbg(vsize, 0, hfname, hlineno) ;
  a->magic = ASS_MAGIC ;
  a->id = ++nAss ;
  a->n = 0 ;
  a->i = 0 ; /* start up position for recursive calls */
  a->m = nbits ;
  a->mask = size - 1 ;
  return a ;
}

/*******************/

Associator assBigCreate_dbg(int size, const char *hfname, int hlineno)
{
  int n = 2 ; /* make room, be twice as big as needed */

  if (size <= 0) 
    uMessSetErrorOrigin(hfname, hlineno),
      uMessCrash ("assBigCreate called with size = %d <= 0", size) ;

  --size ;
  while (size >>= 1) n++ ; /* number of left most bit + 1 */
  return assDoCreate_dbg(n, 0, hfname, hlineno) ;
}

/*******************/

Associator assHandleCreate_dbg(STORE_HANDLE handle,
			       const char *hfname,int hlineno)
{ return assDoCreate_dbg(5, handle, hfname, hlineno) ; }

/*******************/

void assClear (Associator a)
{ mysize_t sz ;
  if (!assExists(a)) 
    return ;
  
  a->n = 0 ;
  sz = ( 1 << a->m) *  VSIZE ;
  memset(a->in, 0, sz) ;
  memset(a->out, 0, sz) ;
}

/********************/

Associator assReCreate_dbg (Associator a,
			    const char *hfname, int hlineno)
{ if (!assExists(a))
    return assHandleCreate_dbg(0, hfname, hlineno) ;
  else
    { assClear (a) ;  return a ; }
}

/********************/

static void assDouble (Associator a)
{ int oldsize, newsize, nbits ;
  register int hash, delta ;
  void **old_in = a->in, **old_out = a->out, **xin, **xout ;
  int i ;

  nbits = a->m ;
  oldsize = 1 << nbits ;
  newsize = oldsize << 1 ; /* size must be a power of 2 */

  a->n = 0 ;
  a->i = 0 ; /* start up position for recursive calls */
  a->m = nbits + 1 ;
  a->mask = newsize - 1 ;
  a->in  = (void**) messalloc (newsize * VSIZE) ;
  a->out = (void**) messalloc (newsize * VSIZE) ;

  for (i = 0, xin = old_in, xout = old_out ; i < oldsize ; i++, xin++, xout++)
    if (*xin && *xin != moins_un)
      { HASH(*xin) ;
        while (TRUE)
          { if (!a->in[hash])  /* don't need to test moins_un */
              { a->in[hash] = *xin ;
                a->out[hash] = *xout ;
                ++a->n ;
                assInserted++ ;
                break ;
              }
            assBounce++ ;
	    DELTA(*xin) ;	/* redo each time - cheap overall */
            hash = (hash + delta) & a->mask ;
          }
      }

  messfree (old_in) ;
  messfree (old_out) ;
}

/************************ Searches  ************************************/

BOOL uAssFind (Associator a, void* xin, void** pout)
/* if found, updates *pout and returns TRUE, else returns FALSE	*/
{ int hash, delta = 0 ;
  void* test ;

  if (!assExists(a))
    messcrash ("assFind received corrupted associator") ;
  if (!xin || xin == moins_un) return FALSE ;

  HASH(xin) ;
  while (TRUE)
    { test = a->in[hash] ;
      if (test == xin)
	{ if (pout)
	    *pout = a->out[hash] ;
	  assFound++ ;
	  a->i = hash ;
	  return TRUE ;
	}
      if (!test)
	break ;
      assBounce++ ;
      if (!delta) DELTA(xin) ;
      hash = (hash + delta) & a->mask ;
    }
  assNotFound++ ;
  return FALSE ;
}

/********************/
  /* Usage: if(uAssFind()){while (uAssFindNext()) ... } */
BOOL uAssFindNext (Associator a, void* xin, void** pout)
/* if found, updates *pout and returns TRUE, else returns FALSE	*/
{ int	hash, delta ;
  void* test ;

  if (!assExists(a))
    messcrash ("assFindNext received corrupted associator") ;
  if (!xin || xin == moins_un || !a->in[a->i]) 
    return FALSE ;
  if (a->in[a->i] != xin)
    messcrash ("Non consecutive call to assFindNext") ;

  hash = a->i ;
  DELTA(xin) ;
  while (TRUE)
    { test = a->in[hash] ;
      if (test == xin)
	{ if (pout)
	    *pout = a->out[hash] ;
	  while (TRUE) /* locate on next entry */
	    { hash = (hash + delta) & a->mask ; 
	      test = a->in[hash] ;
	      if (!test || test == xin)
		break ;
	      assBounce++ ;
	    }
	  a->i = hash ; /* points to next entry or zero */
	  assFound++ ;
	  return TRUE ;
	}
      if (!test)
	break ;
      assBounce++ ;
      hash = (hash + delta) & a->mask ;
    }
  assNotFound++ ;
  return FALSE ;
}

/************************ Insertions  ************************************/

     /* if already there returns FALSE, else inserts and returns TRUE */
static BOOL assDoInsert (Associator a, void* xin, void* xout, BOOL noMultiples)
{ int	hash, delta = 0 ;
  void* test ;

  if (!assExists(a))
    messcrash ("assInsert received corrupted associator") ;

  if (!xin || xin == moins_un) 
    messcrash ("assInsert received forbidden value xin == 0") ;

  if (a->n >= (1 << (a->m - 1))) /* reaching floating line */
    assDouble (a) ;

  HASH (xin) ;
 
  while (TRUE)
    { test = a->in[hash] ;
      if (!test || test == moins_un)  /* reuse deleted slots */
	{ a->in[hash] = xin ;
	  a->out[hash] = xout ;
	  ++a->n ;
	  assInserted++ ;
	  return TRUE ;
	}
      if (noMultiples && test == xin)		/* already there */
	return FALSE ;
      assBounce++ ;
      if (!delta) DELTA (xin) ;
      hash = (hash + delta) & a->mask ;
    }
}

/*****************/
     /* This one does not allow multiple entries in one key. */
BOOL assInsert (Associator a, void* xin, void* xout)
{ return assDoInsert (a, xin, xout, TRUE) ;
}
 
/*****************/

void assMultipleInsert (Associator a, void* xin, void* xout)
     /* This one allows multiple entries in one key. */
{ assDoInsert (a, xin, xout, FALSE) ;
}
 
/************************ Removals ************************************/
   /* if found, removes entry and returns TRUE, else returns FALSE	*/
   /* No a->n--, because the entry is blanked but not actually removed */
BOOL assRemove (Associator a, void* xin)
{ if (assExists(a) && uAssFind (a, xin, 0))
    { a->in[a->i] = moins_un ;
      assRemoved++ ;
      return TRUE ;
    }
  else
    return FALSE ;
}

/*******************/

/* if found, removes entry and returns TRUE, else returns FALSE	*/
/* Requires both xin and xout to match */
BOOL assPairRemove (Associator a, void* xin, void* xout)
{ if (!assExists(a) || !xin || xin == moins_un) return FALSE ;
  if (uAssFind (a, xin, 0))
    while (uAssFindNext (a, xin, 0))
      if (a->out[a->i] == xout)
	{ a->in[a->i] = moins_un ;
	  assRemoved++ ;
	  return TRUE ;
	}
  return FALSE ;
}

/************************ dumpers ********************************/
     /* lets you step through all members of the table */
BOOL uAssNext (Associator a, void* *pin, void* *pout)
{ int size ;
  void *test ;

  if (!assExists(a))
     messcrash("uAssNext received a non existing associator") ;
  size = 1 << a->m ;
  if (!*pin)
    a->i = -1 ;
  else if (*pin != a->in[a->i])
    { messerror ("Non-consecutive call to assNext()") ;
      return FALSE ;
    }

  while (++a->i < size)
    { test = a->in[a->i] ;
      if (test && test != moins_un) /* not empty or deleted */
	{ *pin = a->in[a->i] ;
	  if (pout)
	    *pout = a->out[a->i] ;
	  return TRUE ;
	}
    }
  return FALSE ;
}

/*******************/

void assDump (Associator a)
{ int i ; 
  void **in, **out ;
  char *cp0 = 0 ;

  if (!assExists(a)) return ;

  i = 1 << a->m ;
  in = a->in - 1 ; out = a->out - 1 ;
      /* keep stderr here since it is for debugging */
  fprintf (stderr,"Associator %lx : %d pairs\n",(unsigned long)a,a->n) ;
  while (in++, out++, i--)
    if (*in && *in != moins_un) /* not empty or deleted */
      fprintf (stderr,"%lx - %lx\n",
	       (long)((char*)(*in) - cp0),(long)( (char *)(*out) - cp0)) ;
}

/************************  end of file ********************************/
/**********************************************************************/
 
 
 
 
 
