
/*
** nbench1.c
*/

/********************************
**       BYTEmark (tm)         **
** BYTE NATIVE MODE BENCHMARKS **
**       VERSION 2             **
**                             **
** Included in this source     **
** file:                       **
**  Numeric Heapsort           **
**  String Heapsort            **
**  Bitfield test              **
**  Floating point emulation   **
**  Fourier coefficients       **
**  Assignment algorithm       **
**  IDEA Encyption             **
**  Huffman compression        **
**  Back prop. neural net      **
**  LU Decomposition           **
**    (linear equations)       **
** ----------                  **
** Rick Grehan, BYTE Magazine  **
*********************************
*/

/*
** INCLUDES
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "nmglobal.h"
#include "nbench1.h"
#include "sysspec.h"
#include "wordcat.h"


#ifndef MAC
#include <mem.h>
#endif
/*********************
** NUMERIC HEAPSORT **
**********************
** This test implements a heapsort algorithm, performed on an
** array of longs.
*/

/**************
** DoNumSort **
***************
** This routine performs the CPU numeric sort test.
** NOTE: Last version incorrectly stated that the routine
**  returned result in # of longword sorted per second.
**  Not so; the routine returns # of iterations per sec.
*/

void DoNumSort(void)
{
SortStruct *numsortstruct;      /* Local pointer to global struct */
farlong *arraybase;     /* Base pointers of array */
long accumtime;         /* Accumulated time */
double iterations;      /* Iteration counter */
char *errorcontext;     /* Error context string pointer */
int systemerror;        /* For holding error codes */

/*
** Link to global structure
*/
numsortstruct=&global_numsortstruct;

/*
** Set the error context string.
*/
errorcontext="CPU:Numeric Sort";

/*
** See if we need to do self adjustment code.
*/
if(numsortstruct->adjust==0)
{
        /*
        ** Self-adjustment code.  The system begins by sorting 1
        ** array.  If it does that in no time, then two arrays
        ** are built and sorted.  This process continues until
        ** enough arrays are built to handle the tolerance.
        */
        numsortstruct->numarrays=1;
        while(1)
        {
                /*
                ** Allocate space for arrays
                */
                arraybase=(farlong *)AllocateMemory(sizeof(long) *
                        numsortstruct->numarrays * numsortstruct->arraysize,
                        &systemerror);
                if(systemerror)
                {       ReportError(errorcontext,systemerror);
                        FreeMemory((farvoid *)arraybase,
                                  &systemerror);
                        ErrorExit();
                }

                /*
                ** Do an iteration of the numeric sort.  If the
                ** elapsed time is less than or equal to the permitted
                ** minimum, then allocate for more arrays and
                ** try again.
                */
                if(DoNumSortIteration(arraybase,
                        numsortstruct->arraysize,
                        numsortstruct->numarrays)>global_min_ticks)
                        break;          /* We're ok...exit */

                FreeMemory((farvoid *)arraybase,&systemerror);
                if(numsortstruct->numarrays++>NUMNUMARRAYS)
                {       printf("CPU:NSORT -- NUMNUMARRAYS hit.\n");
                        ErrorExit();
                }
        }
}
else
{       /*
        ** Allocate space for arrays
        */
        arraybase=(farlong *)AllocateMemory(sizeof(long) *
                numsortstruct->numarrays * numsortstruct->arraysize,
                &systemerror);
        if(systemerror)
        {       ReportError(errorcontext,systemerror);
                FreeMemory((farvoid *)arraybase,
                          &systemerror);
                ErrorExit();
        }

}
/*
** All's well if we get here.  Repeatedly perform sorts until the
** accumulated elapsed time is greater than # of seconds requested.
*/
accumtime=0L;
iterations=(double)0.0;

do {
        accumtime+=DoNumSortIteration(arraybase,
                numsortstruct->arraysize,
                numsortstruct->numarrays);
        iterations+=(double)1.0;
} while(TicksToSecs(accumtime)<numsortstruct->request_secs);

/*
** Clean up, calculate results, and go home.  Be sure to
** show that we don't have to rerun adjustment code.
*/
FreeMemory((farvoid *)arraybase,&systemerror);

numsortstruct->sortspersec=iterations * 
        (double)numsortstruct->numarrays / TicksToFracSecs(accumtime);

if(numsortstruct->adjust==0)
        numsortstruct->adjust=1;

return;
}

/***********************
** DoNumSortIteration **
************************
** This routine executes one iteration of the numeric
** sort benchmark.  It returns the number of ticks
** elapsed for the iteration.
*/
static ulong DoNumSortIteration(farlong *arraybase,
                ulong arraysize,
                uint numarrays)
{
ulong elapsed;          /* Elapsed ticks */
ulong i;
/*
** Load up the array with random numbers
*/
LoadNumArrayWithRand(arraybase,arraysize,numarrays);

/*
** Start the stopwatch
*/
elapsed=StartStopwatch();

/*
** Execute a heap of heapsorts
*/
for(i=0;i<numarrays;i++)
        NumHeapSort(arraybase+i*arraysize,0L,arraysize-1L);

/*
** Get elapsed time
*/
elapsed=StopStopwatch(elapsed);
#ifdef DEBUG
{

        for(i=0;i<arraysize-1;i++)
        {       /*
                ** Compare to check for proper
                ** sort.
                */
                if(arraybase[i+1]<arraybase[i])
                {       printf("Sort Error\n");
                        break;
                }
        }
}
#endif

return(elapsed);
}

/*************************
** LoadNumArrayWithRand **
**************************
** Load up an array with random longs.
*/
static void LoadNumArrayWithRand(farlong *array,     /* Pointer to arrays */
                ulong arraysize,
                uint numarrays)         /* # of elements in array */
{
long i;                 /* Used for index */
farlong *darray;        /* Destination array pointer */
/*
** Initialize the random number generator
*/
randnum(13L);

/*
** Load up first array with randoms
*/
for(i=0L;i<arraysize;i++)
        array[i]=randnum(0L);

/*
** Now, if there's more than one array to load, copy the
** first into each of the others.
*/
darray=array;
while(--numarrays)
{       darray+=arraysize;
        for(i=0L;i<arraysize;i++)
                darray[i]=array[i];
}
        
return;
}

/****************
** NumHeapSort **
*****************
** Pass this routine a pointer to an array of long
** integers.  Also pass in minimum and maximum offsets.
** This routine performs a heap sort on that array.
*/
static void NumHeapSort(farlong *array,
        ulong bottom,           /* Lower bound */
        ulong top)              /* Upper bound */
{
ulong temp;                     /* Used to exchange elements */
ulong i;                        /* Loop index */

/*
** First, build a heap in the array
*/
for(i=(top/2L); i>0; --i)
        NumSift(array,i,top);

/*
** Repeatedly extract maximum from heap and place it at the
** end of the array.  When we get done, we'll have a sorted
** array.
*/
for(i=top; i>0; --i)
{       NumSift(array,bottom,i);
        temp=*array;                    /* Perform exchange */
        *array=*(array+i);
        *(array+i)=temp;
}
return;
}

/************
** NumSift **
*************
** Peforms the sift operation on a numeric array,
** constructing a heap in the array.
*/
static void NumSift(farlong *array,     /* Array of numbers */
        ulong i,                /* Minimum of array */
        ulong j)                /* Maximum of array */
{
unsigned long k;
long temp;                              /* Used for exchange */

while((i+i)<=j)
{
        k=i+i;
        if(k<j)
                if(array[k]<array[k+1L])
                        ++k;
        if(array[i]<array[k])
        {
                temp=array[k];
                array[k]=array[i];
                array[i]=temp;
                i=k;
        }
        else
                i=j+1;
}
return;
}

/********************
** STRING HEAPSORT **
********************/

/*****************
** DoStringSort **
******************
** This routine performs the CPU string sort test.
** Arguments:
**      requested_secs = # of seconds to execute test
**      stringspersec = # of strings per second sorted (RETURNED)
*/
void DoStringSort(void)
{

SortStruct *strsortstruct;      /* Local for sort structure */
faruchar *arraybase;            /* Base pointer of char array */
long accumtime;                 /* Accumulated time */
double iterations;              /* # of iterations */
char *errorcontext;             /* Error context string pointer */
int systemerror;                /* For holding error code */

/*
** Link to global structure
*/
strsortstruct=&global_strsortstruct;

/*
** Set the error context
*/
errorcontext="CPU:String Sort";

/*
** See if we have to perform self-adjustment code
*/
if(strsortstruct->adjust==0)
{
        /*
        ** Initialize the number of arrays.
        */
        strsortstruct->numarrays=1;
        while(1)
        {
                /*
                ** Allocate space for array.  We'll add an extra 100
                ** bytes to protect memory as strings move around
                ** (this can happen during string adjustment)
                */
                arraybase=(faruchar *)AllocateMemory((strsortstruct->arraysize+100L) *
                        (long)strsortstruct->numarrays,&systemerror);
                if(systemerror)
                {       ReportError(errorcontext,systemerror);
                        ErrorExit();
                }

                /*
                ** Do an iteration of the string sort.  If the
                ** elapsed time is less than or equal to the permitted
                ** minimum, then de-allocate the array, reallocate a
                ** an additional array, and try again.
                */
                if(DoStringSortIteration(arraybase,
                        strsortstruct->numarrays,
                        strsortstruct->arraysize)>global_min_ticks)
                        break;          /* We're ok...exit */

                FreeMemory((farvoid *)arraybase,&systemerror);
                strsortstruct->numarrays+=1;
        }
}
else
{
        /*
        ** We don't have to perform self adjustment code.
        ** Simply allocate the space for the array.
        */
        arraybase=(faruchar *)AllocateMemory((strsortstruct->arraysize+100L) *
                (long)strsortstruct->numarrays,&systemerror);
        if(systemerror)
        {       ReportError(errorcontext,systemerror);
                ErrorExit();
        }
}
/*
** All's well if we get here.  Repeatedly perform sorts until the
** accumulated elapsed time is greater than # of seconds requested.
*/
accumtime=0L;
iterations=(double)0.0;

do {
        accumtime+=DoStringSortIteration(arraybase,
                                strsortstruct->numarrays,
                                strsortstruct->arraysize);
        iterations+=(double)strsortstruct->numarrays;
} while(TicksToSecs(accumtime)<strsortstruct->request_secs);

/*
** Clean up, calculate results, and go home.
** Set flag to show we don't need to rerun adjustment code.
*/
FreeMemory((farvoid *)arraybase,&systemerror);
strsortstruct->sortspersec=iterations / (double)TicksToFracSecs(accumtime);
if(strsortstruct->adjust==0)
        strsortstruct->adjust=1;
return;
}

/**************************
** DoStringSortIteration **
***************************
** This routine executes one iteration of the string
** sort benchmark.  It returns the number of ticks
** Note that this routine also builds the offset pointer
** array.
*/
static ulong DoStringSortIteration(faruchar *arraybase,
                uint numarrays,ulong arraysize)
{
farulong *optrarray;            /* Offset pointer array */
unsigned long elapsed;          /* Elapsed ticks */
unsigned long nstrings;         /* # of strings in array */
int syserror;                   /* System error code */
unsigned int i;                 /* Index */
farulong *tempobase;            /* Temporary offset pointer base */
faruchar *tempsbase;            /* Temporary string base pointer */

/*
** Load up the array(s) with random numbers
*/
optrarray=LoadStringArray(arraybase,numarrays,&nstrings,arraysize);

/*
** Set temp base pointers...they will be modified as the
** benchmark proceeds.
*/
tempobase=optrarray;
tempsbase=arraybase;
 
/*
** Start the stopwatch
*/
elapsed=StartStopwatch();

/*
** Execute heapsorts
*/
for(i=0;i<numarrays;i++)
{       StrHeapSort(tempobase,tempsbase,nstrings,0L,nstrings-1);
        tempobase+=nstrings;    /* Advance base pointers */
        tempsbase+=arraysize+100;
}

/*
** Record elapsed time
*/
elapsed=StopStopwatch(elapsed);

#ifdef DEBUG
{
        unsigned long i;
        for(i=0;i<nstrings-1;i++)
        {       /*
                ** Compare strings to check for proper
                ** sort.
                */
                if(str_is_less(optrarray,arraybase,nstrings,i+1,i))
                {       printf("Sort Error\n");
                        break;
                }
        }
}
#endif

/*
** Release the offset pointer array built by
** LoadStringArray()
*/
FreeMemory((farvoid *)optrarray,&syserror);

/*
** Return elapsed ticks.
*/
return(elapsed);
}

/********************
** LoadStringArray **
*********************
** Initialize the string array with random strings of
** varying sizes.
** Returns the pointer to the offset pointer array.
** Note that since we're creating a number of arrays, this
** routine builds one array, then copies it into the others.
*/
static farulong *LoadStringArray(faruchar *strarray, /* String array */
        uint numarrays,                 /* # of arrays */
        ulong *nstrings,                /* # of strings */
        ulong arraysize)                /* Size of array */
{
faruchar *tempsbase;            /* Temporary string base pointer */
farulong *optrarray;            /* Local for pointer */
farulong *tempobase;            /* Temporary offset pointer base pointer */
unsigned long curroffset;       /* Current offset */
int fullflag;                   /* Indicates full array */
unsigned char stringlength;     /* Length of string */
unsigned char i;                /* Index */
unsigned long j;                /* Another index */
unsigned int k;                 /* Yet another index */
unsigned int l;                 /* Ans still one more index */
int systemerror;                /* For holding error code */

/*
** Initialize random number generator.
*/
randnum(13L);

/*
** Start with no strings.  Initialize our current offset pointer
** to 0.
*/
*nstrings=0L;
curroffset=0L;
fullflag=0;

do
{
        /*
        ** Allocate a string with a random length no
        ** shorter than 4 bytes and no longer than
        ** 80 bytes.  Note we have to also make sure
        ** there's room in the array.
        */
        stringlength=(unsigned char)(1+abs_randwc(76L) & 0xFFL);
        if((unsigned long)stringlength+curroffset+1L>=arraysize)
        {       stringlength=(unsigned char)((arraysize-curroffset-1L) &
                                0xFF);
                fullflag=1;     /* Indicates a full */
        }

        /*
        ** Store length at curroffset and advance current offset.
        */
        *(strarray+curroffset)=stringlength;
        curroffset++;

        /*
        ** Fill up the rest of the string with random bytes.
        */
        for(i=0;i<stringlength;i++)
        {       *(strarray+curroffset)=
                        (unsigned char)(abs_randwc((long)0xFE));
                curroffset++;
        }

        /*
        ** Increment the # of strings counter.
        */
        *nstrings+=1L;

} while(fullflag==0);

/*
** We now have initialized a single full array.  If there
** is more than one array, copy the original into the
** others.
*/
k=1;
tempsbase=strarray;
while(k<numarrays)
{       tempsbase+=arraysize+100;         /* Set base */
        for(l=0;l<arraysize;l++)
                tempsbase[l]=strarray[l];
        k++;
}

/*
** Now the array is full, allocate enough space for an
** offset pointer array.
*/
optrarray=(farulong *)AllocateMemory(*nstrings * sizeof(unsigned long) *
                numarrays,
                &systemerror);
if(systemerror)
{       ReportError("CPU:Stringsort",systemerror);
        FreeMemory((void *)strarray,&systemerror);
        ErrorExit();
}

/*
** Go through the newly-built string array, building
** offsets and putting them into the offset pointer
** array.
*/
curroffset=0;
for(j=0;j<*nstrings;j++)
{       *(optrarray+j)=curroffset;
        curroffset+=(unsigned long)(*(strarray+curroffset))+1L;
}

/*
** As above, we've made one copy of the offset pointers,
** so duplicate this array in the remaining ones.
*/
k=1;
tempobase=optrarray;
while(k<numarrays)
{       tempobase+=*nstrings;
        for(l=0;l<*nstrings;l++)
                tempobase[l]=optrarray[l];
        k++;
}

/*
** All done...go home.  Pass local pointer back.
*/
return(optrarray);
}

/**************
** stradjust **
***************
** Used by the string heap sort.  Call this routine to adjust the
** string at offset i to length l.  The members of the string array
** are moved accordingly and the length of the string at offset i
** is set to l.
*/
static void stradjust(farulong *optrarray,      /* Offset pointer array */
        faruchar *strarray,                     /* String array */
        ulong nstrings,                         /* # of strings */
        ulong i,                                /* Offset to adjust */
        uchar l)                                /* New length */
{
unsigned long nbytes;           /* # of bytes to move */
unsigned long j;                /* Index */
int direction;                  /* Direction indicator */
unsigned char adjamount;        /* Adjustment amount */

/*
** If new length is less than old length, the direction is
** down.  If new length is greater than old length, the
** direction is up.
*/
direction=(int)l - (int)*(strarray+*(optrarray+i));
adjamount=(unsigned char)abs(direction);

/*
** See if the adjustment is being made to the last
** string in the string array.  If so, we don't have to
** do anything more than adjust the length field.
*/
if(i==(nstrings-1L))
{       *(strarray+*(optrarray+i))=l;
        return;
}

/*
** Calculate the total # of bytes in string array from
** location i+1 to end of array.  Whether we're moving "up" or
** down, this is how many bytes we'll have to move.
*/
nbytes=*(optrarray+nstrings-1L) +
        (unsigned long)*(strarray+*(optrarray+nstrings-1L)) + 1L -
        *(optrarray+i+1L);

/*
** Calculate the source and the destination.  Source is
** string position i+1.  Destination is string position i+l
** (i+"ell"...don't confuse 1 and l).
** Hand this straight to memmove and let it handle the
** "overlap" problem.
*/
MoveMemory((farvoid *)(strarray+*(optrarray+i)+l+1),
        (farvoid *)(strarray+*(optrarray+i+1)),
        (unsigned long)nbytes);

/*
** We have to adjust the offset pointer array.
** This covers string i+1 to numstrings-1.
*/
for(j=i+1;j<nstrings;j++)
        if(direction<0)
                *(optrarray+j)=*(optrarray+j)-adjamount;
        else
                *(optrarray+j)=*(optrarray+j)+adjamount;
        
/*
** Store the new length and go home.
*/
*(strarray+*(optrarray+i))=l;
return;
}

/****************
** strheapsort **
*****************
** Pass this routine a pointer to an array of unsigned char.
** The array is presumed to hold strings occupying at most
** 80 bytes (counts a byte count).
** This routine also needs a pointer to an array of offsets
** which represent string locations in the array, and
** an unsigned long indicating the number of strings
** in the array.
*/
static void StrHeapSort(farulong *optrarray, /* Offset pointers */
        faruchar *strarray,             /* Strings array */
        ulong numstrings,               /* # of strings in array */
        ulong bottom,                   /* Region to sort...bottom */
        ulong top)                      /* Region to sort...top */
{
unsigned char temp[80];                 /* Used to exchange elements */
unsigned char tlen;                     /* Temp to hold length */
unsigned long i;                        /* Loop index */


/*
** Build a heap in the array
*/
for(i=(top/2L); i>0; --i)
        strsift(optrarray,strarray,numstrings,i,top);

/*
** Repeatedly extract maximum from heap and place it at the
** end of the array.  When we get done, we'll have a sorted
** array.
*/
for(i=top; i>0; --i)
{       
        strsift(optrarray,strarray,numstrings,0,i);

        /* temp = string[0] */
        tlen=*strarray;
        MoveMemory((farvoid *)&temp[0], /* Perform exchange */
                (farvoid *)strarray,
                (unsigned long)(tlen+1));


        /* string[0]=string[i] */
        tlen=*(strarray+*(optrarray+i));
        stradjust(optrarray,strarray,numstrings,0,tlen);
        MoveMemory((farvoid *)strarray,
                (farvoid *)(strarray+*(optrarray+i)),
                (unsigned long)(tlen+1));

        /* string[i]=temp */
        tlen=temp[0];
        stradjust(optrarray,strarray,numstrings,i,tlen);
        MoveMemory((farvoid *)(strarray+*(optrarray+i)),
                (farvoid *)&temp[0],
                (unsigned long)(tlen+1));

}
return;
}

/****************
** str_is_less **
*****************
** Pass this function:
**      1) A pointer to an array of offset pointers
**      2) A pointer to a string array
**      3) The number of elements in the string array
**      4) Offsets to two strings (a & b)
** This function returns TRUE if string a is < string b.
*/
static int str_is_less(farulong *optrarray, /* Offset pointers */
        faruchar *strarray,                     /* String array */
        ulong numstrings,                       /* # of strings */
        ulong a, ulong b)                       /* Offsets */
{
int slen;               /* String length */

/*
** Determine which string has the minimum length.  Use that
** to call strncmp().  If they match up to that point, the
** string with the longer length wins.
*/
slen=(int)*(strarray+*(optrarray+a));
if(slen > (int)*(strarray+*(optrarray+b)))
        slen=(int)*(strarray+*(optrarray+b));

slen=strncmp((char *)(strarray+*(optrarray+a)),
                (char *)(strarray+*(optrarray+b)),slen);

if(slen==0)
{
        /*
        ** They match.  Return true if the length of a
        ** is greater than the length of b.
        */
        if(*(strarray+*(optrarray+a)) >
                *(strarray+*(optrarray+b)))
                return(TRUE);
        return(FALSE);
}

if(slen<0) return(TRUE);        /* a is strictly less than b */

return(FALSE);                  /* Only other possibility */
}

/************
** strsift **
*************
** Pass this function:
**      1) A pointer to an array of offset pointers
**      2) A pointer to a string array
**      3) The number of elements in the string array
**      4) Offset within which to sort.
** Sift the array within the bounds of those offsets (thus
** building a heap).
*/
static void strsift(farulong *optrarray,        /* Offset pointers */
        faruchar *strarray,                     /* String array */
        ulong numstrings,                       /* # of strings */
        ulong i, ulong j)                       /* Offsets */
{
unsigned long k;                /* Temporaries */
unsigned char temp[80];
unsigned char tlen;             /* For string lengths */


while((i+i)<=j)
{
        k=i+i;
        if(k<j)
                if(str_is_less(optrarray,strarray,numstrings,k,k+1L))
                        ++k;
        if(str_is_less(optrarray,strarray,numstrings,i,k))
        {
                /* temp=string[k] */
                tlen=*(strarray+*(optrarray+k));
                MoveMemory((farvoid *)&temp[0],
                        (farvoid *)(strarray+*(optrarray+k)),
                        (unsigned long)(tlen+1));

                /* string[k]=string[i] */
                tlen=*(strarray+*(optrarray+i));
                stradjust(optrarray,strarray,numstrings,k,tlen);
                MoveMemory((farvoid *)(strarray+*(optrarray+k)),
                        (farvoid *)(strarray+*(optrarray+i)),
                        (unsigned long)(tlen+1));

                /* string[i]=temp */
                tlen=temp[0];
                stradjust(optrarray,strarray,numstrings,i,tlen);
                MoveMemory((farvoid *)(strarray+*(optrarray+i)),
                        (farvoid *)&temp[0],
                        (unsigned long)(tlen+1));
                i=k;
        }
        else
                i=j+1;
}
return;
}

/************************
** BITFIELD OPERATIONS **
*************************/

/*************
** DoBitops **
**************
** Perform the bit operations test portion of the CPU
** benchmark.  Returns the iterations per second.
*/
void DoBitops(void)
{
BitOpStruct *locbitopstruct;    /* Local bitop structure */
farulong *bitarraybase;         /* Base of bitmap array */
farulong *bitoparraybase;       /* Base of bitmap operations array */
ulong nbitops;                  /* # of bitfield operations */
ulong accumtime;                /* Accumulated time in ticks */
double iterations;              /* # of iterations */
char *errorcontext;             /* Error context string */
int systemerror;                /* For holding error codes */

/*
** Link to global structure.
*/
locbitopstruct=&global_bitopstruct;

/*
** Set the error context.
*/
errorcontext="CPU:Bitfields";

/*
** See if we need to run adjustment code.
*/
if(locbitopstruct->adjust==0)
{
        bitarraybase=(farulong *)AllocateMemory(locbitopstruct->bitfieldarraysize *
                sizeof(ulong),&systemerror);
        if(systemerror)
        {       ReportError(errorcontext,systemerror);
                ErrorExit();
        }

        /*
        ** Initialize bitfield operations array to [2,30] elements
        */
        locbitopstruct->bitoparraysize=30L;

        while(1)
        {
                /*
                ** Allocate space for operations array
                */
                bitoparraybase=(farulong *)AllocateMemory(locbitopstruct->bitoparraysize*2L*
                        sizeof(ulong),
                        &systemerror);
                if(systemerror)
                {       ReportError(errorcontext,systemerror);
                        FreeMemory((farvoid *)bitarraybase,&systemerror);
                        ErrorExit();
                }
                /*
                ** Do an iteration of the bitmap test.  If the
                ** elapsed time is less than or equal to the permitted
                ** minimum, then de-allocate the array, reallocate a
                ** larger version, and try again.
                */
                if(DoBitfieldIteration(bitarraybase,
                        bitoparraybase,
                        locbitopstruct->bitoparraysize,
                        &nbitops)>global_min_ticks)
                break;          /* We're ok...exit */

                FreeMemory((farvoid *)bitoparraybase,&systemerror);
                locbitopstruct->bitoparraysize+=100L;
        }
}
else             
{
        /*
        ** Don't need to do self adjustment, just allocate
        ** the array space.
        */
        bitarraybase=(farulong *)AllocateMemory(locbitopstruct->bitfieldarraysize *
                sizeof(ulong),&systemerror);
        if(systemerror)
        {       ReportError(errorcontext,systemerror);
                ErrorExit();
        }
        bitoparraybase=(farulong *)AllocateMemory(locbitopstruct->bitoparraysize*2L*
                sizeof(ulong),
                &systemerror);
        if(systemerror)
        {       ReportError(errorcontext,systemerror);
                FreeMemory((farvoid *)bitarraybase,&systemerror);
                ErrorExit();
        }
}

/*
** All's well if we get here.  Repeatedly perform bitops until the
** accumulated elapsed time is greater than # of seconds requested.
*/
accumtime=0L;
iterations=(double)0.0;
do {
        accumtime+=DoBitfieldIteration(bitarraybase,
                        bitoparraybase,
                        locbitopstruct->bitoparraysize,&nbitops);
        iterations+=(double)nbitops;
} while(TicksToSecs(accumtime)<locbitopstruct->request_secs);

/*
** Clean up, calculate results, and go home.
** Also, set adjustment flag to show that we don't have
** to do self adjusting in the future.
*/
FreeMemory((farvoid *)bitarraybase,&systemerror);
FreeMemory((farvoid *)bitoparraybase,&systemerror);
locbitopstruct->bitopspersec=iterations /TicksToFracSecs(accumtime);
if(locbitopstruct->adjust==0)
        locbitopstruct->adjust=1;

return;
}

/************************
** DoBitfieldIteration **
*************************
** Perform a single iteration of the bitfield benchmark.
** Return the # of ticks accumulated by the operation.
*/
static ulong DoBitfieldIteration(farulong *bitarraybase,
                farulong *bitoparraybase,
                long bitoparraysize,
                ulong *nbitops)
{
long i;                         /* Index */
ulong bitoffset;                /* Offset into bitmap */
ulong elapsed;                  /* Time to execute */

/*
** Clear # bitops counter
*/
*nbitops=0L;

/*
** Construct a set of bitmap offsets and run lengths.
** The offset can be any random number from 0 to the
** size of the bitmap (in bits).  The run length can
** be any random number from 1 to the number of bits
** between the offset and the end of the bitmap.
** Note that the bitmap has 8192 * 32 bits in it.
** (262,144 bits)
*/
for (i=0;i<bitoparraysize;i++)
{
        /* First item is offset */
        *(bitoparraybase+i+i)=bitoffset=abs_randwc(262140L);

        /* Next item is run length */
        *nbitops+=*(bitoparraybase+i+i+1L)=abs_randwc(262140L-bitoffset);
}

/*
** Array of offset and lengths built...do an iteration of
** the test.
** Start the stopwatch.
*/
elapsed=StartStopwatch();

/*
** Loop through array off offset/run length pairs.
** Execute operation based on modulus of index.
*/
for(i=0;i<bitoparraysize;i++)
{
        switch(i % 3)
        {

                case 0: /* Set run of bits */
                        ToggleBitRun(bitarraybase,
                                *(bitoparraybase+i+i),
                                *(bitoparraybase+i+i+1),
                                1);
                        break;

                case 1: /* Clear run of bits */
                        ToggleBitRun(bitarraybase,
                                *(bitoparraybase+i+i),
                                *(bitoparraybase+i+i+1),
                                0);
                        break;

                case 2: /* Complement run of bits */
                        FlipBitRun(bitarraybase,
                                *(bitoparraybase+i+i),
                                *(bitoparraybase+i+i+1));
                        break;
        }
}

/*
** Return elapsed time
*/
return(StopStopwatch(elapsed));
}


/*****************************
**     ToggleBitRun          *
******************************
** Set or clear a run of nbits starting at
** bit_addr in bitmap.
*/
static void ToggleBitRun(farulong *bitmap, /* Bitmap */
                ulong bit_addr,         /* Address of bits to set */
                ulong nbits,            /* # of bits to set/clr */
                uint val)               /* 1 or 0 */
{
unsigned long bindex;   /* Index into array */
unsigned long bitnumb;  /* Bit number */

while(nbits--)
{
#ifdef LONG64
        bindex=bit_addr>>6;     /* Index is number /64 */
        bindex=bit_addr % 64;   /* Bit number in word */
#else
        bindex=bit_addr>>5;     /* Index is number /32 */
        bitnumb=bit_addr % 32;  /* bit number in word */
#endif
        if(val)
                bitmap[bindex]|=(1L<<bitnumb);
        else
                bitmap[bindex]&=~(1L<<bitnumb);
        bit_addr++;
}
return;
}

/***************
** FlipBitRun **
****************
** Complements a run of bits.
*/
static void FlipBitRun(farulong *bitmap,        /* Bit map */
                ulong bit_addr,                 /* Bit address */
                ulong nbits)                    /* # of bits to flip */
{
unsigned long bindex;   /* Index into array */
unsigned long bitnumb;  /* Bit number */

while(nbits--)
{
#ifdef LONG64
        bindex=bit_addr>>6;     /* Index is number /64 */
        bitnumb=bit_addr % 32;  /* Bit number in longword */
#else
        bindex=bit_addr>>5;     /* Index is number /32 */
        bitnumb=bit_addr % 32;  /* Bit number in longword */
#endif
        bitmap[bindex]^=(1L<<bitnumb);
        bit_addr++;
}

return;
}

/*****************************
** FLOATING-POINT EMULATION **
*****************************/

/**************
** DoEmFloat **
***************
** Perform the floating-point emulation routines portion of the
** CPU benchmark.  Returns the operations per second.
*/
void DoEmFloat(void)
{
EmFloatStruct *locemfloatstruct;        /* Local structure */
InternalFPF *abase;             /* Base of A array */
InternalFPF *bbase;             /* Base of B array */
InternalFPF *cbase;             /* Base of C array */
ulong accumtime;                /* Accumulated time in ticks */
double iterations;              /* # of iterations */
ulong tickcount;                /* # of ticks */
char *errorcontext;             /* Error context string pointer */
int systemerror;                /* For holding error code */
ulong loops;                    /* # of loops */

/*
** Link to global structure
*/
locemfloatstruct=&global_emfloatstruct;

/*
** Set the error context
*/
errorcontext="CPU:Floating Emulation";


/*
** Test the emulation routines.
*/
#ifdef DEBUG
#endif

abase=(InternalFPF *)AllocateMemory(locemfloatstruct->arraysize*sizeof(InternalFPF),
                &systemerror);
if(systemerror)
{       ReportError(errorcontext,systemerror);
        ErrorExit();
}

bbase=(InternalFPF *)AllocateMemory(locemfloatstruct->arraysize*sizeof(InternalFPF),
                &systemerror);
if(systemerror)
{       ReportError(errorcontext,systemerror);
        FreeMemory((farvoid *)abase,&systemerror);
        ErrorExit();
}

cbase=(InternalFPF *)AllocateMemory(locemfloatstruct->arraysize*sizeof(InternalFPF),
                &systemerror);
if(systemerror)
{       ReportError(errorcontext,systemerror);
        FreeMemory((farvoid *)abase,&systemerror);
        FreeMemory((farvoid *)bbase,&systemerror);
        ErrorExit();
}

/*
** Set up the arrays
*/
SetupCPUEmFloatArrays(abase,bbase,cbase,locemfloatstruct->arraysize);

/*
** See if we need to do self-adjusting code.
*/
if(locemfloatstruct->adjust==0)
{
        locemfloatstruct->loops=0;

        /*
        ** Do an iteration of the tests.  If the elapsed time is
        ** less than minimum, increase the loop count and try
        ** again. 
        */
        for(loops=1;loops<CPUEMFLOATLOOPMAX;loops+=loops)
        {       tickcount=DoEmFloatIteration(abase,bbase,cbase,
                        locemfloatstruct->arraysize,
                        loops);
                if(tickcount>global_min_ticks)
                {       locemfloatstruct->loops=loops;
                        break;
                }
        }
}

/*
** Verify that selft adjustment code worked.
*/
if(locemfloatstruct->loops==0)
{       printf("CPU:EMFPU -- CMPUEMFLOATLOOPMAX limit hit\n");
        FreeMemory((farvoid *)abase,&systemerror);
        FreeMemory((farvoid *)bbase,&systemerror);
        FreeMemory((farvoid *)cbase,&systemerror);
        ErrorExit();
}

/*
** All's well if we get here.  Repeatedly perform floating
** tests until the accumulated time is greater than the
** # of seconds requested.
** Each iteration performs arraysize * 3 operations.
*/
accumtime=0L;
iterations=(double)0.0;
do {
        accumtime+=DoEmFloatIteration(abase,bbase,cbase,
                        locemfloatstruct->arraysize,
                        locemfloatstruct->loops);
        iterations+=(double)1.0;
} while(TicksToSecs(accumtime)<locemfloatstruct->request_secs);


/*
** Clean up, calculate results, and go home.
** Also, indicate that adjustment is done.
*/
FreeMemory((farvoid *)abase,&systemerror);
FreeMemory((farvoid *)bbase,&systemerror);
FreeMemory((farvoid *)cbase,&systemerror);

locemfloatstruct->emflops=(iterations*(double)locemfloatstruct->loops)/
                (double)TicksToFracSecs(accumtime);
if(locemfloatstruct->adjust==0)
        locemfloatstruct->adjust=1;

return;
}

/*************************
** FOURIER COEFFICIENTS **
*************************/

/**************
** DoFourier **
***************
** Perform the transcendental/trigonometric portion of the
** benchmark.  This benchmark calculates the first n
** fourier coefficients of the function (x+1)^x defined
** on the interval 0,2.
*/
void DoFourier(void)
{
FourierStruct *locfourierstruct;        /* Local fourier struct */
fardouble *abase;               /* Base of A[] coefficients array */
fardouble *bbase;               /* Base of B[] coefficients array */
unsigned long accumtime;        /* Accumulated time in ticks */
double iterations;              /* # of iterations */
char *errorcontext;             /* Error context string pointer */
int systemerror;                /* For error code */

/*
** Link to global structure
*/
locfourierstruct=&global_fourierstruct;

/*
** Set error context string
*/
errorcontext="FPU:Transcendental";

/*
** See if we need to do self-adjustment code.
*/
if(locfourierstruct->adjust==0)
{
        locfourierstruct->arraysize=100L;       /* Start at 100 elements */
        while(1)
        {

                abase=(fardouble *)AllocateMemory(locfourierstruct->arraysize*sizeof(double),
                                &systemerror);
                if(systemerror)
                {       ReportError(errorcontext,systemerror);
                        ErrorExit();
                }

                bbase=(fardouble *)AllocateMemory(locfourierstruct->arraysize*sizeof(double),
                                &systemerror);
                if(systemerror)
                {       ReportError(errorcontext,systemerror);
                        FreeMemory((void *)abase,&systemerror);
                        ErrorExit();
                }
                /*
                ** Do an iteration of the tests.  If the elapsed time is
                ** less than or equal to the permitted minimum, re-allocate
                ** larger arrays and try again.
                */
                if(DoFPUTransIteration(abase,bbase,
                        locfourierstruct->arraysize)>global_min_ticks)
                        break;          /* We're ok...exit */
        
                /*
                ** Make bigger arrays and try again.
                */
                FreeMemory((farvoid *)abase,&systemerror);
                FreeMemory((farvoid *)bbase,&systemerror);
                locfourierstruct->arraysize+=50L;
        }
}
else
{       /*
        ** Don't need self-adjustment.  Just allocate the
        ** arrays, and go.
        */
        abase=(fardouble *)AllocateMemory(locfourierstruct->arraysize*sizeof(double),
                        &systemerror);
        if(systemerror)
        {       ReportError(errorcontext,systemerror);
                ErrorExit();
        }

        bbase=(fardouble *)AllocateMemory(locfourierstruct->arraysize*sizeof(double),
                        &systemerror);
        if(systemerror)
        {       ReportError(errorcontext,systemerror);
                FreeMemory((void *)abase,&systemerror);
                ErrorExit();
        }
}
/*
** All's well if we get here.  Repeatedly perform integration
** tests until the accumulated time is greater than the
** # of seconds requested.
*/
accumtime=0L;
iterations=(double)0.0;
do {
        accumtime+=DoFPUTransIteration(abase,bbase,locfourierstruct->arraysize);
        iterations+=(double)locfourierstruct->arraysize*(double)2.0-(double)1.0;
} while(TicksToSecs(accumtime)<locfourierstruct->request_secs);                         


/*
** Clean up, calculate results, and go home.
** Also set adjustment flag to indicate no adjust code needed.
*/
FreeMemory((farvoid *)abase,&systemerror);
FreeMemory((farvoid *)bbase,&systemerror);

locfourierstruct->fflops=iterations/(double)TicksToFracSecs(accumtime);

if(locfourierstruct->adjust==0)
        locfourierstruct->adjust=1;

return;
}

/************************
** DoFPUTransIteration **
*************************
** Perform an iteration of the FPU Transcendental/trigonometric
** benchmark.  Here, an iteration consists of calculating the
** first n fourier coefficients of the function (x+1)^x on
** the interval 0,2.  n is given by arraysize.
** NOTE: The # of integration steps is fixed at
** 200.
*/
static ulong DoFPUTransIteration(fardouble *abase,      /* A coeffs. */
                        fardouble *bbase,               /* B coeffs. */
                        ulong arraysize)                /* # of coeffs */
{
double omega;           /* Fundamental frequency */
unsigned long i;        /* Index */
unsigned long elapsed;  /* Elapsed time */

/*
** Start the stopwatch
*/
elapsed=StartStopwatch();

/*
** Calculate the fourier series.  Begin by
** calculating A[0].
*/

*abase=TrapezoidIntegrate((double)0.0,
                        (double)2.0,
                        200,
                        (double)0.0,    /* No omega * n needed */
                        0 )/(double)2.0;

/*
** Calculate the fundamental frequency.
** ( 2 * pi ) / period...and since the period
** is 2, omega is simply pi.
*/
omega=(double)3.1415926535897932;

for(i=1;i<arraysize;i++)
{

        /*
        ** Calculate A[i] terms.  Note, once again, that we
        ** can ignore the 2/period term outside the integral
        ** since the period is 2 and the term cancels itself
        ** out.
        */
        *(abase+i)=TrapezoidIntegrate((double)0.0,
                        (double)2.0,
                        200,
                        omega * (double)i,
                        1);     

        /*
        ** Calculate the B[i] terms.
        */
        *(bbase+i)=TrapezoidIntegrate((double)0.0,
                        (double)2.0,
                        200,
                        omega * (double)i,
                        2);

}

/*
** All done, stop the stopwatch
*/
return(StopStopwatch(elapsed));
}

/***********************
** TrapezoidIntegrate **
************************
** Perform a simple trapezoid integration on the
** function (x+1)**x.
** x0,x1 set the lower and upper bounds of the
** integration.
** nsteps indicates # of trapezoidal sections
** omegan is the fundamental frequency times
**  the series member #
** select = 0 for the A[0] term, 1 for cosine terms, and
**   2 for sine terms.
** Returns the value.
*/
static double TrapezoidIntegrate( double x0,            /* Lower bound */
                        double x1,              /* Upper bound */
                        int nsteps,             /* # of steps */
                        double omegan,          /* omega * n */
                        int select)
{
double x;               /* Independent variable */
double dx;              /* Stepsize */
double rvalue;          /* Return value */


/*
** Initialize independent variable
*/
x=x0;

/*
** Calculate stepsize
*/
dx=(x1 - x0) / (double)nsteps;

/*
** Initialize the return value.
*/
rvalue=thefunction(x0,omegan,select)/(double)2.0;

/*
** Compute the other terms of the integral.
*/
if(nsteps!=1)
{       --nsteps;               /* Already done 1 step */
        while(--nsteps )
        {
                x+=dx;
                rvalue+=thefunction(x,omegan,select);
        }
}
/*
** Finish computation
*/
rvalue=(rvalue+thefunction(x1,omegan,select)/(double)2.0)*dx;

return(rvalue);
}

/****************
** thefunction **
*****************
** This routine selects the function to be used
** in the Trapezoid integration.
** x is the independent variable
** omegan is omega * n
** select chooses which of the sine/cosine functions
**  are used.  note the special case for select=0.
*/
static double thefunction(double x,             /* Independent variable */
                double omegan,          /* Omega * term */
                int select)             /* Choose term */
{

/*
** Use select to pick which function we call.
*/
switch(select)
{
        case 0: return(pow(x+(double)1.0,x));

        case 1: return(pow(x+(double)1.0,x) * cos(omegan * x));

        case 2: return(pow(x+(double)1.0,x) * sin(omegan * x));
}

/*
** We should never reach this point, but the following
** keeps compilers from issuing a warning message.
*/
return(0.0);
}

/*************************
** ASSIGNMENT ALGORITHM **
*************************/

/*************
** DoAssign **
**************
** Perform an assignment algorithm.
** The algorithm was adapted from the step by step guide found
** in "Quantitative Decision Making for Business" (Gordon,
**  Pressman, and Cohn; Prentice-Hall)
**
**
** NOTES:
** 1. Even though the algorithm distinguishes between
**    ASSIGNROWS and ASSIGNCOLS, as though the two might
**    be different, it does presume a square matrix.
**    I.E., ASSIGNROWS and ASSIGNCOLS must be the same.
**    This makes for some algorithmically-correct but
**    probably non-optimal constructs.
**
*/
void DoAssign(void)
{
AssignStruct *locassignstruct;  /* Local structure ptr */
farlong *arraybase;
char *errorcontext;
int systemerror;
ulong accumtime;
double iterations;

/*
** Link to global structure
*/
locassignstruct=&global_assignstruct;

/*
** Set the error context string.
*/
errorcontext="CPU:Assignment";                   

/*
** See if we need to do self adjustment code.
*/
if(locassignstruct->adjust==0)
{
        /*
        ** Self-adjustment code.  The system begins by working on 1
        ** array.  If it does that in no time, then two arrays
        ** are built.  This process continues until
        ** enough arrays are built to handle the tolerance.
        */
        locassignstruct->numarrays=1;
        while(1)
        {
                /*
                ** Allocate space for arrays
                */
                arraybase=(farlong *) AllocateMemory(sizeof(long)*
                        ASSIGNROWS*ASSIGNCOLS*locassignstruct->numarrays,
                         &systemerror);
                if(systemerror)
                {       ReportError(errorcontext,systemerror);
                        FreeMemory((farvoid *)arraybase,
                          &systemerror);
                        ErrorExit();
                }

                /*
                ** Do an iteration of the assignment alg.  If the
                ** elapsed time is less than or equal to the permitted
                ** minimum, then allocate for more arrays and
                ** try again.
                */
                if(DoAssignIteration(arraybase,
                        locassignstruct->numarrays)>global_min_ticks)
                        break;          /* We're ok...exit */

                FreeMemory((farvoid *)arraybase, &systemerror);
                locassignstruct->numarrays++;
        }
}
else
{       /*
        ** Allocate space for arrays
        */
        arraybase=(farlong *)AllocateMemory(sizeof(long)*
                ASSIGNROWS*ASSIGNCOLS*locassignstruct->numarrays,
                 &systemerror);
        if(systemerror)
        {       ReportError(errorcontext,systemerror);
                FreeMemory((farvoid *)arraybase,
                  &systemerror);
                ErrorExit();
        }
}

/*
** All's well if we get here.  Do the tests.
*/
accumtime=0L;
iterations=(double)0.0;

do {
        accumtime+=DoAssignIteration(arraybase,
                locassignstruct->numarrays);
        iterations+=(double)1.0;
} while(TicksToSecs(accumtime)<locassignstruct->request_secs);

/*
** Clean up, calculate results, and go home.  Be sure to
** show that we don't have to rerun adjustment code.
*/
FreeMemory((farvoid *)arraybase,&systemerror);

locassignstruct->iterspersec=iterations * 
        (double)locassignstruct->numarrays / TicksToFracSecs(accumtime);

if(locassignstruct->adjust==0)
        locassignstruct->adjust=1;

return;

}

/**********************
** DoAssignIteration **
***********************
** This routine executes one iteration of the assignment test.
** It returns the number of ticks elapsed in the iteration.
*/
static ulong DoAssignIteration(farlong *arraybase,
        ulong numarrays)
{
longptr abase;                  /* local pointer */
ulong elapsed;          /* Elapsed ticks */
ulong i;

/*
** Set up local pointer
*/
abase.ptrs.p=arraybase;

/*
** Load up the arrays with a random table.
*/
LoadAssignArrayWithRand(arraybase,numarrays);

/*
** Start the stopwatch
*/
elapsed=StartStopwatch();

/*
** Execute assignment algorithms
*/
for(i=0;i<numarrays;i++)
{       abase.ptrs.p+=i*ASSIGNROWS*ASSIGNCOLS;
        Assignment(*abase.ptrs.ap);
}

/*
** Get elapsed time
*/
return(StopStopwatch(elapsed));
}

/****************************
** LoadAssignArrayWithRand **
*****************************
** Load the assignment arrays with random numbers.  All positive.
** These numbers represent costs.
*/
static void LoadAssignArrayWithRand(farlong *arraybase,
        ulong numarrays)
{
longptr abase,abase1;   /* Local for array pointer */
ulong i;

/*
** Set local array pointer
*/
abase.ptrs.p=arraybase;
abase1.ptrs.p=arraybase;

/*
** Set up the first array.  Then just copy it into the
** others.
*/
LoadAssign(*(abase.ptrs.ap));
if(numarrays>1)
        for(i=1;i<numarrays;i++)
        {       abase1.ptrs.p+=i*ASSIGNROWS*ASSIGNCOLS;
                CopyToAssign(*(abase.ptrs.ap),*(abase1.ptrs.ap));
        }

return;
}

/***************
** LoadAssign **
****************
** The array given by arraybase is loaded with positive random
** numbers.  Elements in the array are capped at 5,000,000.
*/
static void LoadAssign(farlong arraybase[][ASSIGNCOLS])
{
ushort i,j;

/*
** Reset random number generator so things repeat.
*/
randnum(13L);

for(i=0;i<ASSIGNROWS;i++)
        for(j=0;j<ASSIGNROWS;j++)
                arraybase[i][j]=abs_randwc(5000000L);
return;
}

/*****************
** CopyToAssign **
******************
** Copy the contents of one array to another.  This is called by
** the routine that builds the initial array, and is used to copy
** the contents of the intial array into all following arrays.
*/
static void CopyToAssign(farlong arrayfrom[ASSIGNROWS][ASSIGNCOLS],
                farlong arrayto[ASSIGNROWS][ASSIGNCOLS])
{
ushort i,j;

for(i=0;i<ASSIGNROWS;i++)
        for(j=0;j<ASSIGNCOLS;j++)
                arrayto[i][j]=arrayfrom[i][j];

return;
}

/***************
** Assignment **
***************/
static void Assignment(farlong arraybase[][ASSIGNCOLS])
{
static short assignedtableau[ASSIGNROWS][ASSIGNCOLS];

/*
** First, calculate minimum costs
*/
calc_minimum_costs(arraybase);

/*
** Repeat following until the number of rows selected
** equals the number of rows in the tableau.
*/
while(first_assignments(arraybase,assignedtableau)!=ASSIGNROWS)
{         second_assignments(arraybase,assignedtableau);
}

#ifdef DEBUG
{
        int i,j;
        printf("Column choices for each row\n");
        for(i=0;i<ASSIGNROWS;i++)
        {
                for(j=0;j<ASSIGNCOLS;j++)
                        if(assignedtableau[i][j]!=0)
                                printf("%d ",j);
        }
}
#endif

return;
}

/***********************
** calc_minimum_costs **
************************
** Revise the tableau by calculating the minimum costs on a
** row and column basis.  These minima are subtracted from
** their rows and columns, creating a new tableau.
*/
static void calc_minimum_costs(long tableau[][ASSIGNCOLS])
{
ushort i,j;              /* Index variables */     
long currentmin;        /* Current minimum */        
/*
** Determine minimum costs on row basis.  This is done by
** subtracting -- on a row-per-row basis -- the minum value
** for that row.
*/
for(i=0;i<ASSIGNROWS;i++)
{
        currentmin=MAXPOSLONG;  /* Initialize minimum */
        for(j=0;j<ASSIGNCOLS;j++)
                if(tableau[i][j]<currentmin)
                        currentmin=tableau[i][j];

        for(j=0;j<ASSIGNCOLS;j++)
                tableau[i][j]-=currentmin;
}

/*
** Determine minimum cost on a column basis.  This works
** just as above, only now we step through the array
** column-wise
*/
for(j=0;j<ASSIGNCOLS;j++)
{
        currentmin=MAXPOSLONG;  /* Initialize minimum */
        for(i=0;i<ASSIGNROWS;i++)
                if(tableau[i][j]<currentmin)
                        currentmin=tableau[i][j];

        /*
        ** Here, we'll take the trouble to see if the current
        ** minimum is zero.  This is likely worth it, since the
        ** preceding loop will have created at least one zero in
        ** each row.  We can save ourselves a few iterations.
        */
        if(currentmin!=0)
                for(i=0;i<ASSIGNROWS;i++)
                        tableau[i][j]-=currentmin;
}

return;
}

/**********************
** first_assignments **
***********************
** Do first assignments.
** The assignedtableau[] array holds a set of values that
** indicate the assignment of a value, or its elimination.
** The values are:
**      0 = Item is neither assigned nor eliminated.
**      1 = Item is assigned
**      2 = Item is eliminated
** Returns the number of selections made.  If this equals
** the number of rows, then an optimum has been determined.
*/
static int first_assignments(long tableau[][ASSIGNCOLS],
                short assignedtableau[][ASSIGNCOLS])
{
ushort i,j,k;                   /* Index variables */
ushort numassigns;              /* # of assignments */
ushort totnumassigns;           /* Total # of assignments */
ushort numzeros;                /* # of zeros in row */
int selected;                   /* Flag used to indicate selection */    

/*
** Clear the assignedtableau, setting all members to show that
** no one is yet assigned, eliminated, or anything.
*/
for(i=0;i<ASSIGNROWS;i++)
        for(j=0;j<ASSIGNCOLS;j++)
                assignedtableau[i][j]=0;

totnumassigns=0;                
do {
        numassigns=0;
        /*
        ** Step through rows.  For each one that is not currently
        ** assigned, see if the row has only one zero in it.  If so,
        ** mark that as an assigned row/col.  Eliminate other zeros
        ** in the same column.
        */
        for(i=0;i<ASSIGNROWS;i++)
        {       numzeros=0;
                for(j=0;j<ASSIGNCOLS;j++)
                        if(tableau[i][j]==0L)
                                if(assignedtableau[i][j]==0)
                                {       numzeros++;
                                        selected=j;
                                }
                if(numzeros==1)
                {       numassigns++;
                        totnumassigns++;
                        assignedtableau[i][selected]=1;
                        for(k=0;k<ASSIGNROWS;k++)
                                if((k!=i) &&
                                   (tableau[k][selected]==0))
                                        assignedtableau[k][selected]=2;
                }
        }
        /*
        ** Step through columns, doing same as above.  Now, be careful
        ** of items in the other rows of a selected column.
        */
        for(j=0;j<ASSIGNCOLS;j++)
        {       numzeros=0;
                for(i=0;i<ASSIGNROWS;i++)
                        if(tableau[i][j]==0L)
                                if(assignedtableau[i][j]==0)
                                {       numzeros++;
                                        selected=i;
                                }
                if(numzeros==1)
                {       numassigns++;
                        totnumassigns++;
                        assignedtableau[selected][j]=1;
                        for(k=0;k<ASSIGNCOLS;k++)
                                if((k!=j) &&
                                   (tableau[selected][k]==0))
                                        assignedtableau[selected][k]=2;
                }
        }
        /*
        ** Repeat until no more assignments to be made.
        */
} while(numassigns!=0);

/*
** See if we can leave at this point.
*/
if(totnumassigns==ASSIGNROWS) return(totnumassigns);

/*
** Now step through the array by row.  If you find any unassigned
** zeros, pick the first in the row.  Eliminate all zeros from
** that same row & column.  This occurs if there are multiple optima...
** possibly.
*/
for(i=0;i<ASSIGNROWS;i++)
{       selected=-1;
        for(j=0;j<ASSIGNCOLS;j++)
                if((tableau[i][j]==0L) &&
                   (assignedtableau[i][j]==0))
                {       selected=j;
                        break;
                }
        if(selected!=-1)
        {       assignedtableau[i][selected]=1;
                totnumassigns++;
                for(k=0;k<ASSIGNCOLS;k++)
                        if((k!=selected) &&
                           (tableau[i][k]==0L))
                                assignedtableau[i][k]=2;
                for(k=0;k<ASSIGNROWS;k++)
                        if((k!=i) &&
                           (tableau[k][selected]==0L))
                                assignedtableau[k][selected]=2;
        }
}

return(totnumassigns);
}

/***********************
** second_assignments **
************************
** This section of the algorithm creates the revised
** tableau, and is difficult to explain.  I suggest you
** refer to the algorithm's source, mentioned in comments
** toward the beginning of the program.
*/
static void second_assignments(long tableau[][ASSIGNCOLS],
                short assignedtableau[][ASSIGNCOLS])
{
int i,j;                                /* Indexes */  
short linesrow[ASSIGNROWS];
short linescol[ASSIGNCOLS];
long smallest;                          /* Holds smallest value */
ushort numassigns;                      /* Number of assignments */
ushort newrows;                         /* New rows to be considered */  
/*
** Clear the linesrow and linescol arrays.
*/
for(i=0;i<ASSIGNROWS;i++)
        linesrow[i]=0;
for(i=0;i<ASSIGNCOLS;i++)
        linescol[i]=0;

/*
** Scan rows, flag each row that has no assignment in it.
*/
for(i=0;i<ASSIGNROWS;i++)
{       numassigns=0;
        for(j=0;j<ASSIGNCOLS;j++)
                if(assignedtableau[i][j]==1)
                {       numassigns++;
                        break;
                }
        if(numassigns==0) linesrow[i]=1;
}

do {

        newrows=0;
        /*
        ** For each row checked above, scan for any zeros.  If found,
        ** check the associated column.
        */
        for(i=0;i<ASSIGNROWS;i++)
        {       if(linesrow[i]==1)
                        for(j=0;j<ASSIGNCOLS;j++)
                                if(tableau[i][j]==0)
                                        linescol[j]=1;
        }

        /*
        ** Now scan checked columns.  If any contain assigned zeros, check
        ** the associated row.
        */
        for(j=0;j<ASSIGNCOLS;j++)
                if(linescol[j]==1)
                        for(i=0;i<ASSIGNROWS;i++)
                                if((assignedtableau[i][j]==1) &&
                                        (linesrow[i]!=1))
                                {
                                        linesrow[i]=1;
                                        newrows++;
                                }
} while(newrows!=0);

/*
** linesrow[n]==0 indicate rows covered by imaginary line
** linescol[n]==1 indicate cols covered by imaginary line
** For all cells not covered by imaginary lines, determine smallest
** value.
*/
smallest=MAXPOSLONG;
for(i=0;i<ASSIGNROWS;i++)
        if(linesrow[i]!=0)
                for(j=0;j<ASSIGNCOLS;j++)
                        if(linescol[j]!=1)
                                if(tableau[i][j]<smallest)
                                        smallest=tableau[i][j];

/*
** Subtract smallest from all cells in the above set.
*/
for(i=0;i<ASSIGNROWS;i++)
        if(linesrow[i]!=0)
                for(j=0;j<ASSIGNCOLS;j++)
                        if(linescol[j]!=1)
                                tableau[i][j]-=smallest;

/*
** Add smallest to all cells covered by two lines.
*/
for(i=0;i<ASSIGNROWS;i++)
        if(linesrow[i]==0)
                for(j=0;j<ASSIGNCOLS;j++)
                        if(linescol[j]==1)
                                tableau[i][j]+=smallest;

return;
}

/********************
** IDEA Encryption **
*********************
** IDEA - International Data Encryption Algorithm.
** Based on code presented in Applied Cryptography by Bruce Schneier.
** Which was based on code developed by Xuejia Lai and James L. Massey.
** Other modifications made by Colin Plumb.
**
*/

/***********
** DoIDEA **
************
** Perform IDEA encryption.  Note that we time encryption & decryption
** time as being a single loop.
*/
void DoIDEA(void)
{
IDEAStruct *locideastruct;      /* Loc pointer to global structure */
int i;     
IDEAkey Z,DK;
u16 userkey[8];
ulong accumtime;
double iterations;
char *errorcontext;
int systemerror;
faruchar *plain1;               /* First plaintext buffer */
faruchar *crypt1;               /* Encryption buffer */
faruchar *plain2;               /* Second plaintext buffer */

/*
** Link to global data
*/
locideastruct=&global_ideastruct;

/*
** Set error context
*/
errorcontext="CPU:IDEA";

/*
** Re-init random-number generator.
*/
randnum(3L);

/*
** Build an encryption/decryption key
*/
for (i=0;i<8;i++)
        userkey[i]=(u16)(abs_randwc(60000L) & 0xFFFF);
for(i=0;i<KEYLEN;i++)
        Z[i]=0;
                
/*
** Compute encryption/decryption subkeys
*/
en_key_idea(userkey,Z);
de_key_idea(Z,DK);

/*
** Allocate memory for buffers.  We'll make 3, called plain1,
** crypt1, and plain2.  It works like this:
**   plain1 >>encrypt>> crypt1 >>decrypt>> plain2.
** So, plain1 and plain2 should match.
** Also, fill up plain1 with sample text.       
*/                                              
plain1=(faruchar *)AllocateMemory(locideastruct->arraysize,&systemerror);
if(systemerror)
{
        ReportError(errorcontext,systemerror);
        ErrorExit();
}

crypt1=(faruchar *)AllocateMemory(locideastruct->arraysize,&systemerror);
if(systemerror)
{
        ReportError(errorcontext,systemerror);
        FreeMemory((farvoid *)plain1,&systemerror);
        ErrorExit();
}

plain2=(faruchar *)AllocateMemory(locideastruct->arraysize,&systemerror);
if(systemerror)
{
        ReportError(errorcontext,systemerror);
        FreeMemory((farvoid *)plain1,&systemerror);
        FreeMemory((farvoid *)crypt1,&systemerror);
        ErrorExit();
}
/*
** Note that we build the "plaintext" by simply loading
** the array up with random numbers.
*/
for(i=0;i<locideastruct->arraysize;i++)
        plain1[i]=(uchar)(abs_randwc(255) & 0xFF);

/*
** See if we need to perform self adjustment loop.
*/
if(locideastruct->adjust==0)
{
        /*
        ** Do self-adjustment.  This involves initializing the
        ** # of loops and increasing the loop count until we
        ** get a number of loops that we can use.
        */
        for(locideastruct->loops=100L;
          locideastruct->loops<MAXIDEALOOPS;
          locideastruct->loops+=10L)
                if(DoIDEAIteration(plain1,crypt1,plain2,
                  locideastruct->arraysize,
                  locideastruct->loops,
                  Z,DK)>global_min_ticks) break;
}

/*
** All's well if we get here.  Do the test.
*/
accumtime=0L;                
iterations=(double)0.0;

do {
        accumtime+=DoIDEAIteration(plain1,crypt1,plain2,
                locideastruct->arraysize,
                locideastruct->loops,Z,DK);
        iterations+=(double)locideastruct->loops;
} while(TicksToSecs(accumtime)<locideastruct->request_secs);

/*
** Clean up, calculate results, and go home.  Be sure to
** show that we don't have to rerun adjustment code.
*/
FreeMemory((farvoid *)plain1,&systemerror);
FreeMemory((farvoid *)crypt1,&systemerror);
FreeMemory((farvoid *)plain2,&systemerror);
locideastruct->iterspersec=iterations / TicksToFracSecs(accumtime);

if(locideastruct->adjust==0)
        locideastruct->adjust=1;

return;

}

/********************
** DoIDEAIteration **
*********************
** Execute a single iteration of the IDEA encryption algorithm.
** Actually, a single iteration is one encryption and one
** decryption.
*/
static ulong DoIDEAIteration(faruchar *plain1,
                        faruchar *crypt1,
                        faruchar *plain2,
                        ulong arraysize,
                        ulong nloops,
                        IDEAkey Z,
                        IDEAkey DK)
{       
register ulong i;
register ulong j;
ulong elapsed;

/*
** Start the stopwatch.
*/
elapsed=StartStopwatch();

/*
** Do everything for nloops.
*/
for(i=0;i<nloops;i++)
{
        for(j=0;j<arraysize;j+=(sizeof(u16)*4))
                cipher_idea((u16 *)(plain1+j),(u16 *)(crypt1+j),Z);       /* Encrypt */

        for(j=0;j<arraysize;j+=(sizeof(u16)*4))
                cipher_idea((u16 *)(crypt1+j),(u16 *)(plain2+j),DK);      /* Decrypt */
}

#ifdef DEBUG
for(j=0;j<arraysize;j++)
        if(*(plain1+j)!=*(plain2+j))
                printf("IDEA Error! \n");
#endif

/*
** Get elapsed time.
*/
return(StopStopwatch(elapsed));
}       
                
/********
** mul **
*********
** Performs multiplication, modulo (2**16)+1.  This code is structured
** on the assumption that untaken branches are cheaper than taken
** branches, and that the compiler doesn't schedule branches.
*/
static u16 mul(register u16 a, register u16 b)
{
register u32 p;
if(a)
{       if(b)
        {       p=(u32)(a*b);
                b=low16(p);
                a=(u16)(p>>16);
                return((u16)(b-a+(b<a)));
        }
        else
                return((u16)(1-a));
}
else
        return((u16)(1-b));
}

/********
** inv **
*********
** Compute multiplicative inverse of x, modulo (2**16)+1
** using Euclid's GCD algorithm.  It is unrolled twice
** to avoid swapping the meaning of the registers.  And
** some subtracts are changed to adds.
*/
static u16 inv(u16 x)
{
u16 t0, t1;
u16 q, y;

if(x<=1)
        return(x);      /* 0 and 1 are self-inverse */
t1=0x10001 / x;
y=0x10001 % x;
if(y==1)
        return((u16)low16(1-t1));
t0=1;
do {
        q=x/y;
        x=x%y;
        t0+=q*t1;
        if(x==1) return(t0);
        q=y/x;
        y=y%x;
        t1+=q*t0;
} while(y!=1);
return((u16)low16(1-t1));
}

/****************
** en_key_idea **
*****************
** Compute IDEA encryption subkeys Z
*/
static void en_key_idea(u16 *userkey, u16 *Z)
{
int i,j;

/*
** shifts
*/
for(j=0;j<8;j++)
        Z[j]=*userkey++;
for(i=0;j<KEYLEN;j++)
{       i++;
        Z[i+7]=(Z[i&7]<<9)| (Z[(i+1) & 7] >> 7);
        Z+=i&8;
        i&=7;
}
return;
}

/****************
** de_key_idea **
*****************
** Compute IDEA decryption subkeys DK from encryption
** subkeys Z.
*/
static void de_key_idea(IDEAkey Z, IDEAkey DK)
{
IDEAkey TT;
int j;
u16 t1, t2, t3;
u16 *p;
p=(u16 *)(TT+KEYLEN);

t1=inv(*Z++);
t2=-*Z++;
t3=-*Z++;
*--p=inv(*Z++);
*--p=t3;
*--p=t2;
*--p=t1;

for(j=1;j<ROUNDS;j++)
{       t1=*Z++;
        *--p=*Z++;
        *--p=t1;
        t1=inv(*Z++);
        t2=-*Z++;
        t3=-*Z++;
        *--p=inv(*Z++);
        *--p=t2;
        *--p=t3;
        *--p=t1;
}
t1=*Z++;
*--p=*Z++;
*--p=t1;
t1=inv(*Z++);
t2=-*Z++;
t3=-*Z++;
*--p=inv(*Z++);
*--p=t3;
*--p=t2;
*--p=t1;
/*
** Copy and destroy temp copy
*/
for(j=0,p=TT;j<KEYLEN;j++)
{       *DK++=*p;
        *p++=0;
}

return;
}

/*
** MUL(x,y)
** This #define creates a macro that computes x=x*y modulo 0x10001.
** Requires temps t16 and t32.  Also requires y to be strictly 16
** bits.  Here, I am using the simplest form.  May not be the
** fastest. -- RG
*/
/* #define MUL(x,y) (x=mul(low16(x),y)) */

/****************
** cipher_idea **
*****************
** IDEA encryption/decryption algorithm.
*/
static void cipher_idea(u16 in[4],
                u16 out[4],
                register IDEAkey Z)
{
register u16 x1, x2, x3, x4, t1, t2;
/* register u16 t16;
register u16 t32; */
int r=ROUNDS;

x1=*in++;
x2=*in++;
x3=*in++;
x4=*in;

do {
        MUL(x1,*Z++);
        x2+=*Z++;
        x3+=*Z++;
        MUL(x4,*Z++);

        t2=x1^x3;
        MUL(t2,*Z++);
        t1=t2+(x2^x4);
        MUL(t1,*Z++);
        t2=t1+t2;

        x1^=t1;
        x4^=t2;
        
        t2^=x2;
        x2=x3^t1;
        x3=t2;
} while(--r);
MUL(x1,*Z++);
*out++=x1;
*out++=x3+*Z++;
*out++=x2+*Z++;
MUL(x4,*Z);
*out=x4;
return;
}

/************************
** HUFFMAN COMPRESSION **
************************/

/**************
** DoHuffman **
***************
** Execute a huffman compression on a block of plaintext.
** Note that (as with IDEA encryption) an iteration of the
** Huffman test includes a compression AND a decompression.
** Also, the compression cycle includes building the
** Huffman tree.
*/
void DoHuffman(void)
{
HuffStruct *lochuffstruct;      /* Loc pointer to global data */
char *errorcontext;
int systemerror;
ulong accumtime;
double iterations;
farchar *comparray;
farchar *decomparray;
farchar *plaintext;

/*
** Link to global data
*/
lochuffstruct=&global_huffstruct;

/*
** Set error context.
*/
errorcontext="CPU:Huffman";

/*
** Allocate memory for the plaintext and the compressed text.
** We'll be really pessimistic here, and allocate equal amounts
** for both (though we know...well, we PRESUME) the compressed
** stuff will take less than the plain stuff.
** Also note that we'll build a 3rd buffer to decompress
** into, and we preallocate space for the huffman tree.
** (We presume that the Huffman tree will grow no larger
** than 512 bytes.  This is actually a super-conservative
** estimate...but, who cares?)
*/
plaintext=(farchar *)AllocateMemory(lochuffstruct->arraysize,&systemerror);
if(systemerror)
{       ReportError(errorcontext,systemerror);
        ErrorExit();
}
comparray=(farchar *)AllocateMemory(lochuffstruct->arraysize,&systemerror);
if(systemerror)
{       ReportError(errorcontext,systemerror);
        FreeMemory(plaintext,&systemerror);
        ErrorExit();
}
decomparray=(farchar *)AllocateMemory(lochuffstruct->arraysize,&systemerror);
if(systemerror)
{       ReportError(errorcontext,systemerror);
        FreeMemory(plaintext,&systemerror);
        FreeMemory(comparray,&systemerror);
        ErrorExit();
}

hufftree=(huff_node *)AllocateMemory(sizeof(huff_node) * 512,
        &systemerror);
if(systemerror)
{       ReportError(errorcontext,systemerror);
        FreeMemory(plaintext,&systemerror);
        FreeMemory(comparray,&systemerror);
        FreeMemory(decomparray,&systemerror);
        ErrorExit();
}

/*
** Build the plaintext buffer.  Since we want this to
** actually be able to compress, we'll use the
** wordcatalog to build the plaintext stuff.
*/
create_text_block(plaintext,lochuffstruct->arraysize-1,(ushort)500);
plaintext[lochuffstruct->arraysize-1L]='\0';
plaintextlen=lochuffstruct->arraysize;

/*
** See if we need to perform self adjustment loop.
*/
if(lochuffstruct->adjust==0)
{
        /*
        ** Do self-adjustment.  This involves initializing the
        ** # of loops and increasing the loop count until we
        ** get a number of loops that we can use.
        */
        for(lochuffstruct->loops=100L;
          lochuffstruct->loops<MAXHUFFLOOPS;
          lochuffstruct->loops+=10L)
                if(DoHuffIteration(plaintext,
                        comparray,
                        decomparray,         
                  lochuffstruct->arraysize,
                  lochuffstruct->loops,
                  hufftree)>global_min_ticks) break;
}

/*
** All's well if we get here.  Do the test.
*/
accumtime=0L;
iterations=(double)0.0;

do {
        accumtime+=DoHuffIteration(plaintext,
                comparray,
                decomparray,
                lochuffstruct->arraysize,
                lochuffstruct->loops,
                hufftree);
        iterations+=(double)lochuffstruct->loops;
} while(TicksToSecs(accumtime)<lochuffstruct->request_secs);

/*
** Clean up, calculate results, and go home.  Be sure to
** show that we don't have to rerun adjustment code.
*/
FreeMemory((farvoid *)plaintext,&systemerror);
FreeMemory((farvoid *)comparray,&systemerror);
FreeMemory((farvoid *)decomparray,&systemerror);
FreeMemory((farvoid *)hufftree,&systemerror);
lochuffstruct->iterspersec=iterations / TicksToFracSecs(accumtime);

if(lochuffstruct->adjust==0)
        lochuffstruct->adjust=1;

}

/*********************
** create_text_line **
**********************
** Create a random line of text, stored at *dt.  The line may be
** no more than nchars long.
*/
static void create_text_line(farchar *dt,
                        long nchars)
{
long charssofar;        /* # of characters so far */
long tomove;            /* # of characters to move */
char myword[40];        /* Local buffer for words */
farchar *wordptr;       /* Pointer to word from catalog */       

charssofar=0;

do {
/*
** Grab a random word from the wordcatalog
*/
wordptr=wordcatarray[abs_randwc((long)WORDCATSIZE)];
MoveMemory((farvoid *)myword,
        (farvoid *)wordptr,
        (unsigned long)strlen(wordptr)+1);

/*
** Append a blank.
*/
tomove=strlen(myword)+1;
myword[tomove-1]=' ';

/*
** See how long it is.  If its length+charssofar > nchars, we have
** to trim it.
*/
if((tomove+charssofar)>nchars)
        tomove=nchars-charssofar;
/*
** Attach the word to the current line.  Increment counter.
*/
MoveMemory((farvoid *)dt,(farvoid *)myword,(unsigned long)tomove);
charssofar+=tomove;
dt+=tomove;

/*
** If we're done, bail out.  Otherwise, go get another word.
*/
} while(charssofar<nchars);

return;
}

/**********************
** create_text_block **
***********************
** Build a block of text randomly loaded with words.  The words
** come from the wordcatalog (which must be loaded before you
** call this).
** *tb points to the memory where the text is to be built.
** tblen is the # of bytes to put into the text block
** maxlinlen is the maximum length of any line (line end indicated
**  by a carriage return).
*/
static void create_text_block(farchar *tb,
                        ulong tblen,
                        ushort maxlinlen)
{
ulong bytessofar;       /* # of bytes so far */
ulong linelen;          /* Line length */

bytessofar=0L;  
do {

/*
** Pick a random length for a line and fill the line.
** Make sure the line can fit (haven't exceeded tablen) and also
** make sure you leave room to append a carriage return.
*/
linelen=abs_randwc(maxlinlen-6)+6;
if((linelen+bytessofar)>tblen)
        linelen=tblen-bytessofar;

if(linelen>1)
{
        create_text_line(tb,linelen);
}
tb+=linelen-1;          /* Add the carriage return */
*tb++='\n';

bytessofar+=linelen;

} while(bytessofar<tblen);

}

/********************
** DoHuffIteration **
*********************
** Perform the huffman benchmark.  This routine
**  (a) Builds the huffman tree
**  (b) Compresses the text
**  (c) Decompresses the text and verifies correct decompression
*/
static ulong DoHuffIteration(farchar *plaintext,
        farchar *comparray,
        farchar *decomparray,
        ulong arraysize,
        ulong nloops,
        huff_node *hufftree)
{
int i;                          /* Index */
long j;                         /* Bigger index */
int root;                       /* Pointer to huffman tree root */
float lowfreq1, lowfreq2;       /* Low frequency counters */
int lowidx1, lowidx2;           /* Indexes of low freq. elements */
long bitoffset;                 /* Bit offset into text */
long textoffset;                /* Char offset into text */
long maxbitoffset;              /* Holds limit of bit offset */
long bitstringlen;              /* Length of bitstring */      
int c;                          /* Character from plaintext */
char bitstring[30];             /* Holds bitstring */   
ulong elapsed;                  /* For stopwatch */

/*
** Start the stopwatch
*/
elapsed=StartStopwatch();

/*
** Do everything for nloops
*/
while(nloops--)
{

/*
** Calculate the frequency of each byte value. Store the
** results in what will become the "leaves" of the
** Huffman tree.  Interior nodes will be built in those
** nodes greater than node #255.
*/
for(i=0;i<256;i++)
{
        hufftree[i].freq=(float)0.0;
        hufftree[i].c=(unsigned char)i;
}

for(j=0;j<arraysize;j++)
        hufftree[plaintext[j]].freq+=(float)1.0;

for(i=0;i<256;i++)
        if(hufftree[i].freq != (float)0.0)
                hufftree[i].freq/=(float)arraysize;

/*
** Build the huffman tree.  First clear all the parent
** pointers and left/right pointers.  Also, discard all
** nodes that have a frequency of true 0.
*/
for(i=0;i<512;i++)
{       if(hufftree[i].freq==(float)0.0)
                hufftree[i].parent=EXCLUDED;
        else
                hufftree[i].parent=hufftree[i].left=hufftree[i].right=-1;
}

/*
** Go through the tree. Finding nodes of really low
** frequency.
*/
root=255;                       /* Starting root node-1 */
while(1)
{
        lowfreq1=(float)2.0; lowfreq2=(float)2.0;
        lowidx1=-1; lowidx2=-1;
        /*
        ** Find first lowest frequency.
        */
        for(i=0;i<=root;i++)
                if(hufftree[i].parent<0)
                        if(hufftree[i].freq<lowfreq1)
                        {       lowfreq1=hufftree[i].freq;
                                lowidx1=i;
                        }

        /*
        ** Did we find a lowest value?  If not, the
        ** tree is done.
        */
        if(lowidx1==-1) break;

        /*
        ** Find next lowest frequency
        */
        for(i=0;i<=root;i++)
                if((hufftree[i].parent<0) && (i!=lowidx1))
                        if(hufftree[i].freq<lowfreq2)
                        {       lowfreq2=hufftree[i].freq;
                                lowidx2=i;
                        }

        /*
        ** If we could only find one item, then that
        ** item is surely the root, and (as above) the
        ** tree is done.
        */
        if(lowidx2==-1) break;
                
        /*
        ** Attach the two new nodes to the current root, and
        ** advance the current root.
        */
        root++;                 /* New root */ 
        hufftree[lowidx1].parent=root;
        hufftree[lowidx2].parent=root;
        hufftree[root].freq=lowfreq1+lowfreq2;
        hufftree[root].left=lowidx1;
        hufftree[root].right=lowidx2;
        hufftree[root].parent=-2;       /* Show root */
}

/*
** Huffman tree built...compress the plaintext
*/
bitoffset=0L;                           /* Initialize bit offset */
for(i=0;i<arraysize;i++)
{
        c=(int)plaintext[i];                 /* Fetch character */
        /*
        ** Build a bit string for byte c
        */
        bitstringlen=0;
        while(hufftree[c].parent!=-2)
        {       if(hufftree[hufftree[c].parent].left==c)
                        bitstring[bitstringlen]='0';
                else
                        bitstring[bitstringlen]='1';
                c=hufftree[c].parent;
                bitstringlen++;
        }

        /*
        ** Step backwards through the bit string, setting
        ** bits in the compressed array as you go.
        */
        while(bitstringlen--)
        {       SetCompBit((u8 *)comparray,(u32)bitoffset,bitstring[bitstringlen]);
                bitoffset++;
        }
}

/*
** Compression done.  Perform de-compression.
*/
maxbitoffset=bitoffset;
bitoffset=0;
textoffset=0;
do {
        i=root;
        while(hufftree[i].left!=-1)
        {       if(GetCompBit((u8 *)comparray,(u32)bitoffset)==0)
                        i=hufftree[i].left;
                else
                        i=hufftree[i].right;
                bitoffset++;
        }
        decomparray[textoffset]=hufftree[i].c;

#ifdef DEBUG
        if(hufftree[i].c != plaintext[textoffset])
        {
                /* Show error */
                printf("Error at textoffset %d\n",textoffset);
        }
#endif
        textoffset++;
} while(bitoffset<maxbitoffset);

}       /* End the big while(nloops--) from above */

/*
** All done
*/
return(StopStopwatch(elapsed));
}

/***************
** SetCompBit **
****************
** Set a bit in the compression array.  The value of the
** bit is set according to char bitchar.
*/
static void SetCompBit(u8 *comparray,
                u32 bitoffset,
                char bitchar)
{
u32 byteoffset;
int bitnumb;

/*
** First calculate which element in the comparray to
** alter. and the bitnumber.
*/
byteoffset=bitoffset>>3;
bitnumb=bitoffset % 8;

/*
** Set or clear
*/
if(bitchar=='1')
        comparray[byteoffset]|=(1<<bitnumb);
else
        comparray[byteoffset]&=~(1<<bitnumb);

return;
}

/***************
** GetCompBit **
****************
** Return the bit value of a bit in the comparession array.
** Returns 0 if the bit is clear, nonzero otherwise.
*/
static int GetCompBit(u8 *comparray,
                u32 bitoffset)
{
u32 byteoffset;
int bitnumb;

/*
** Calculate byte offset and bit number.
*/
byteoffset=bitoffset>>3;
bitnumb=bitoffset % 8;

/*
** Fetch
*/
return((1<<bitnumb) & comparray[byteoffset] );
}

/********************************
** BACK PROPAGATION NEURAL NET **
*********************************
** This code is a modified version of the code
** that was submitted to BYTE Magazine by
** Maureen Caudill.  It accomanied an article
** that I CANNOT NOW RECALL.
** The author's original heading/comment was
** as follows:
**
**  Backpropagation Network
**  Written by Maureen Caudill
**  in Think C 4.0 on a Macintosh
**
**  (c) Maureen Caudill 1988-1991
**  This network will accept 5x7 input patterns
**  and produce 8 bit output patterns.
**  The source code may be copied or modified without restriction,
**  but no fee may be charged for its use.
**
** ++++++++++++++
** I have modified the code so that it will work
** on systems other than a Macintosh -- RG
*/

/***********
** DoNNet **
************
** Perform the neural net benchmark.
** Note that this benchmark is one of the few that
** requires an input file.  That file is "NNET.DAT" and
** should be on the local directory (from which the
** benchmark program in launched).
*/
void DoNNET(void)
{
NNetStruct *locnnetstruct;      /* Local ptr to global data */
char *errorcontext;
int systemerror;
ulong accumtime;
double iterations;

/*
** Link to global data
*/
locnnetstruct=&global_nnetstruct;

/*
** Set error context
*/
errorcontext="CPU:NNET";

/*
** Init random number generator.
** NOTE: It is important that the random number generator
**  be re-initialized for every pass through this test.
**  The NNET algorithm uses the random number generator
**  to initialize the net.  Results are sensitive to
**  the initial neural net state.
*/
randnum(3L);

/*
** Read in the input and output patterns.  We'll do this
** only once here at the beginning.  These values don't
** change once loaded.
*/
if(read_data_file()!=0)
   ErrorExit();


/*
** See if we need to perform self adjustment loop.
*/
if(locnnetstruct->adjust==0)
{
        /*
        ** Do self-adjustment.  This involves initializing the
        ** # of loops and increasing the loop count until we
        ** get a number of loops that we can use.
        */
        for(locnnetstruct->loops=1L;
          locnnetstruct->loops<MAXNNETLOOPS;
          locnnetstruct->loops++)
          {     randnum(3L);
                if(DoNNetIteration(locnnetstruct->loops)
                        >global_min_ticks) break;
          }
}

/*
** All's well if we get here.  Do the test.
*/
accumtime=0L;             
iterations=(double)0.0;

do {
        randnum(3L);    /* Gotta do this for Neural Net */
        accumtime+=DoNNetIteration(locnnetstruct->loops);
        iterations+=(double)locnnetstruct->loops;
} while(TicksToSecs(accumtime)<locnnetstruct->request_secs);

/*
** Clean up, calculate results, and go home.  Be sure to
** show that we don't have to rerun adjustment code.
*/
locnnetstruct->iterspersec=iterations / TicksToFracSecs(accumtime);

if(locnnetstruct->adjust==0)
        locnnetstruct->adjust=1;


return;
}

/********************
** DoNNetIteration **
*********************
** Do a single iteration of the neural net benchmark.
** By iteration, we mean a "learning" pass.
*/
static ulong DoNNetIteration(ulong nloops)
{
ulong elapsed;          /* Elapsed time */
int patt;

/*
** Run nloops learning cycles.  Notice that, counted with
** the learning cycle is the weight randomization and
** zeroing of changes.  This should reduce clock jitter,
** since we don't have to stop and start the clock for
** each iteration.
*/
elapsed=StartStopwatch();
while(nloops--)
{
        randomize_wts();
        zero_changes();
        iteration_count=1;
        learned = F;
        numpasses = 0;
        while (learned == F)
        {
                for (patt=0; patt<numpats; patt++)
                {
                        worst_error = 0.0;      /* reset this every pass through data */
                        move_wt_changes();      /* move last pass's wt changes to momentum array */
                        do_forward_pass(patt);
                        do_back_pass(patt);
                        iteration_count++;
                }
                numpasses ++;
                learned = check_out_error();
        }
#ifdef DEBUG
printf("Learned in %d passes\n",numpasses);
#endif
}
return(StopStopwatch(elapsed));
}

/*************************
** do_mid_forward(patt) **
**************************
** Process the middle layer's forward pass
** The activation of middle layer's neurode is the weighted
** sum of the inputs from the input pattern, with sigmoid
** function applied to the inputs.
**/
static void  do_mid_forward(int patt)
{
double  sum;
int     neurode, i;

for (neurode=0;neurode<MID_SIZE; neurode++)
{
        sum = 0.0;
        for (i=0; i<IN_SIZE; i++)
        {       /* compute weighted sum of input signals */
                sum += mid_wts[neurode][i]*in_pats[patt][i];
        }
        /*
        ** apply sigmoid function f(x) = 1/(1+exp(-x)) to weighted sum
        */
        sum = 1.0/(1.0+exp(-sum));
        mid_out[neurode] = sum;
}
return;
}

/*********************
** do_out_forward() **
**********************
** process the forward pass through the output layer
** The activation of the output layer is the weighted sum of
** the inputs (outputs from middle layer), modified by the
** sigmoid function.
**/
static void  do_out_forward()
{
double sum;
int neurode, i;

for (neurode=0; neurode<OUT_SIZE; neurode++)
{
        sum = 0.0;
        for (i=0; i<MID_SIZE; i++)
        {       /*
                ** compute weighted sum of input signals
                ** from middle layer
                */
                sum += out_wts[neurode][i]*mid_out[i];
        }
        /*
        ** Apply f(x) = 1/(1+exp(-x)) to weighted input
        */
        sum = 1.0/(1.0+exp(-sum));
        out_out[neurode] = sum;
}
return;
}

/*************************
** display_output(patt) **
**************************
** Display the actual output vs. the desired output of the
** network.
** Once the training is complete, and the "learned" flag set
** to TRUE, then display_output sends its output to both
** the screen and to a text output file.
**
** NOTE: This routine has been disabled in the benchmark
** version. -- RG
**/
/*
void  display_output(int patt)
{
int             i;

        fprintf(outfile,"\n Iteration # %d",iteration_count);
        fprintf(outfile,"\n Desired Output:  ");

        for (i=0; i<OUT_SIZE; i++)
        {
                fprintf(outfile,"%6.3f  ",out_pats[patt][i]);
        }
        fprintf(outfile,"\n Actual Output:   ");

        for (i=0; i<OUT_SIZE; i++)
        {
                fprintf(outfile,"%6.3f  ",out_out[i]);
        }
        fprintf(outfile,"\n");
        return;
}
*/

/**********************
** do_forward_pass() **
***********************
** control function for the forward pass through the network
** NOTE: I have disabled the call to display_output() in
**  the benchmark version -- RG.
**/
static void  do_forward_pass(int patt)
{
do_mid_forward(patt);   /* process forward pass, middle layer */
do_out_forward();       /* process forward pass, output layer */
/* display_output(patt);        ** display results of forward pass */
return;
}

/***********************
** do_out_error(patt) **
************************
** Compute the error for the output layer neurodes.
** This is simply Desired - Actual.
**/
static void do_out_error(int patt)
{
int neurode;
double error,tot_error, sum;

tot_error = 0.0;
sum = 0.0;
for (neurode=0; neurode<OUT_SIZE; neurode++)
{
        out_error[neurode] = out_pats[patt][neurode] - out_out[neurode];
        /*
        ** while we're here, also compute magnitude
        ** of total error and worst error in this pass.
        ** We use these to decide if we are done yet.
        */
        error = out_error[neurode];
        if (error <0.0)
        {
                sum += -error;
                if (-error > tot_error)
                        tot_error = -error; /* worst error this pattern */
        }
        else
        {
                sum += error;
                if (error > tot_error)
                        tot_error = error; /* worst error this pattern */
        }
}
avg_out_error[patt] = sum/OUT_SIZE;
tot_out_error[patt] = tot_error;
return;
}

/***********************
** worst_pass_error() **
************************
** Find the worst and average error in the pass and save it
**/
static void  worst_pass_error()
{
double error,sum;

int i;

error = 0.0;
sum = 0.0;
for (i=0; i<numpats; i++)
{
        if (tot_out_error[i] > error) error = tot_out_error[i];
        sum += avg_out_error[i];
}
worst_error = error;
average_error = sum/numpats;
return;
}

/*******************
** do_mid_error() **
********************
** Compute the error for the middle layer neurodes
** This is based on the output errors computed above.
** Note that the derivative of the sigmoid f(x) is
**        f'(x) = f(x)(1 - f(x))
** Recall that f(x) is merely the output of the middle
** layer neurode on the forward pass.
**/
static void do_mid_error()
{
double sum;
int neurode, i;

for (neurode=0; neurode<MID_SIZE; neurode++)
{
        sum = 0.0;
        for (i=0; i<OUT_SIZE; i++)
                sum += out_wts[i][neurode]*out_error[i];

        /*
        ** apply the derivative of the sigmoid here
        ** Because of the choice of sigmoid f(I), the derivative
        ** of the sigmoid is f'(I) = f(I)(1 - f(I))
        */
        mid_error[neurode] = mid_out[neurode]*(1-mid_out[neurode])*sum;
}
return;
}

/*********************
** adjust_out_wts() **
**********************
** Adjust the weights of the output layer.  The error for
** the output layer has been previously propagated back to
** the middle layer.
** Use the Delta Rule with momentum term to adjust the weights.
**/
static void adjust_out_wts()
{
int weight, neurode;
double learn,delta,alph;

learn = BETA;
alph  = ALPHA;
for (neurode=0; neurode<OUT_SIZE; neurode++)
{
        for (weight=0; weight<MID_SIZE; weight++)
        {
                /* standard delta rule */
                delta = learn * out_error[neurode] * mid_out[weight];

                /* now the momentum term */
                delta += alph * out_wt_change[neurode][weight];
                out_wts[neurode][weight] += delta;

                /* keep track of this pass's cum wt changes for next pass's momentum */
                out_wt_cum_change[neurode][weight] += delta;
        }
}
return;
}

/*************************
** adjust_mid_wts(patt) **
**************************
** Adjust the middle layer weights using the previously computed
** errors.
** We use the Generalized Delta Rule with momentum term
**/
static void adjust_mid_wts(int patt)
{
int weight, neurode;
double learn,alph,delta;

learn = BETA;
alph  = ALPHA;
for (neurode=0; neurode<MID_SIZE; neurode++)
{
        for (weight=0; weight<IN_SIZE; weight++)
        {
                /* first the basic delta rule */
                delta = learn * mid_error[neurode] * in_pats[patt][weight];

                /* with the momentum term */
                delta += alph * mid_wt_change[neurode][weight];
                mid_wts[neurode][weight] += delta;

                /* keep track of this pass's cum wt changes for next pass's momentum */
                mid_wt_cum_change[neurode][weight] += delta;
        }
}
return;
}

/*******************
** do_back_pass() **
********************
** Process the backward propagation of error through network.
**/
void  do_back_pass(int patt)
{

do_out_error(patt);
do_mid_error();
adjust_out_wts();
adjust_mid_wts(patt);

return;
}


/**********************
** move_wt_changes() **
***********************
** Move the weight changes accumulated last pass into the wt-change
** array for use by the momentum term in this pass. Also zero out
** the accumulating arrays after the move.
**/
static void move_wt_changes()
{
int i,j;

for (i = 0; i<MID_SIZE; i++)
        for (j = 0; j<IN_SIZE; j++)
        {
                mid_wt_change[i][j] = mid_wt_cum_change[i][j];
                /*
                ** Zero it out for next pass accumulation.
                */
                mid_wt_cum_change[i][j] = 0.0;
        }

for (i = 0; i<OUT_SIZE; i++)
        for (j=0; j<MID_SIZE; j++)
        {
                out_wt_change[i][j] = out_wt_cum_change[i][j];
                out_wt_cum_change[i][j] = 0.0;
        }

return;
}

/**********************
** check_out_error() **
***********************
** Check to see if the error in the output layer is below
** MARGIN*OUT_SIZE for all output patterns.  If so, then
** assume the network has learned acceptably well.  This
** is simply an arbitrary measure of how well the network
** has learned -- many other standards are possible.
**/
static int check_out_error()
{
int result,i,error;

result  = T;
error   = F;
worst_pass_error();     /* identify the worst error in this pass */

/*
#ifdef DEBUG
printf("\n Iteration # %d",iteration_count);
#endif
*/
for (i=0; i<numpats; i++)
{
/*      printf("\n Error pattern %d:   Worst: %8.3f; Average: %8.3f",
          i+1,tot_out_error[i], avg_out_error[i]);
        fprintf(outfile,
         "\n Error pattern %d:   Worst: %8.3f; Average: %8.3f",
         i+1,tot_out_error[i]);
*/

        if (worst_error >= STOP) result = F;
        if (tot_out_error[i] >= 16.0) error = T;
}

if (error == T) result = ERR;


#ifdef DEBUG
/* printf("\n Error this pass thru data:   Worst: %8.3f; Average: %8.3f",
 worst_error,average_error);
*/
/* fprintf(outfile,
 "\n Error this pass thru data:   Worst: %8.3f; Average: %8.3f",
  worst_error, average_error); */
#endif

return(result);
}


/*******************
** zero_changes() **
********************
** Zero out all the wt change arrays
**/
static void zero_changes()
{
int i,j;

for (i = 0; i<MID_SIZE; i++)
{
        for (j=0; j<IN_SIZE; j++)
        {
                mid_wt_change[i][j] = 0.0;
                mid_wt_cum_change[i][j] = 0.0;
        }
}

for (i = 0; i< OUT_SIZE; i++)
{
        for (j=0; j<MID_SIZE; j++)
        {
                out_wt_change[i][j] = 0.0;
                out_wt_cum_change[i][j] = 0.0;
        }
}
return;
}


/********************
** randomize_wts() **
*********************
** Intialize the weights in the middle and output layers to
** random values between -0.25..+0.25
** Function rand() returns a value between 0 and 32767.
**
** NOTE: Had to make alterations to how the random numbers were
** created.  -- RG.
**/
static void randomize_wts()
{
int neurode,i;
double value;

/*
** Following not used int benchmark version -- RG
**
**        printf("\n Please enter a random number seed (1..32767):  ");
**        scanf("%d", &i);
**        srand(i);
*/
for (neurode = 0; neurode<MID_SIZE; neurode++)
{
        for(i=0; i<IN_SIZE; i++)
        {
                value=(double)abs_randwc(100000L);
                value=value/(double)100000.0 - (double) 0.5;
                mid_wts[neurode][i] = value/2;
        }
}

for (neurode=0; neurode<OUT_SIZE; neurode++)
{
        for(i=0; i<MID_SIZE; i++)
        {
                value=(double)abs_randwc(100000L);
                value=value/(double)10000.0 - (double) 0.5;
                out_wts[neurode][i] = value/2;
        }
}
return;
}


/*********************
** read_data_file() **
**********************
** Read in the input data file and store the patterns in
** in_pats and out_pats.
** The format for the data file is as follows:
**
** line#   data expected
** -----   ------------------------------
** 1               In-X-size,in-y-size,out-size
** 2               number of patterns in file
** 3               1st X row of 1st input pattern
** 4..             following rows of 1st input pattern pattern
**                 in-x+2  y-out pattern
**                                 1st X row of 2nd pattern
**                 etc.
**
** Each row of data is separated by commas or spaces.
** The data is expected to be ascii text corresponding to
** either a +1 or a 0.
**
** Sample input for a 1-pattern file (The comments to the
** right may NOT be in the file unless more sophisticated
** parsing of the input is done.):
**
** 5,7,8                      input is 5x7 grid, output is 8 bits
** 1                          one pattern in file
** 0,1,1,1,0                  beginning of pattern for "O"
** 1,0,0,0,1
** 1,0,0,0,1
** 1,0,0,0,1
** 1,0,0,0,1
** 1,0,0,0,0
** 0,1,1,1,0
** 0,1,0,0,1,1,1,1            ASCII code for "O" -- 0100 1111
**
** Clearly, this simple scheme can be expanded or enhanced
** any way you like.
**
** Returns -1 if any file error occurred, otherwise 0.
**/
static int read_data_file()
{
FILE *infile;

int xinsize,yinsize,youtsize;
int patt, element, i, row;
int vals_read;
int val1,val2,val3,val4,val5,val6,val7,val8;

/* printf("\n Opening and retrieving data from file."); */

infile = fopen(inpath, "r");
if (infile == NULL)
{
        printf("\n CPU:NNET--error in opening file!");
        return -1 ;
}
vals_read =fscanf(infile,"%d  %d  %d",&xinsize,&yinsize,&youtsize);
if (vals_read != 3)
{
        printf("\n CPU:NNET -- Should read 3 items in line one; did read %d",vals_read);
        return -1;
}
vals_read=fscanf(infile,"%d",&numpats);
if (vals_read !=1)
{
        printf("\n CPU:NNET -- Should read 1 item in line 2; did read &d",vals_read);
        return -1;
}
if (numpats > MAXPATS)
        numpats = MAXPATS;

for (patt=0; patt<numpats; patt++)
{
        element = 0;
        for (row = 0; row<yinsize; row++)
        {
                vals_read = fscanf(infile,"%d  %d  %d  %d  %d",
                        &val1, &val2, &val3, &val4, &val5);
                if (vals_read != 5)
                {
                        printf ("\n CPU:NNET -- failure in reading input!");
                        return -1;
                }
                element=row*xinsize;

                in_pats[patt][element] = (double) val1; element++;
                in_pats[patt][element] = (double) val2; element++;
                in_pats[patt][element] = (double) val3; element++;
                in_pats[patt][element] = (double) val4; element++;
                in_pats[patt][element] = (double) val5; element++;
        }
        for (i=0;i<IN_SIZE; i++)
        {
                if (in_pats[patt][i] >= 0.9)
                        in_pats[patt][i] = 0.9;
                if (in_pats[patt][i] <= 0.1)
                        in_pats[patt][i] = 0.1;
        }
        element = 0;
        vals_read = fscanf(infile,"%d  %d  %d  %d  %d  %d  %d  %d",
                &val1, &val2, &val3, &val4, &val5, &val6, &val7, &val8);

        out_pats[patt][element] = (double) val1; element++;
        out_pats[patt][element] = (double) val2; element++;
        out_pats[patt][element] = (double) val3; element++;
        out_pats[patt][element] = (double) val4; element++;
        out_pats[patt][element] = (double) val5; element++;
        out_pats[patt][element] = (double) val6; element++;
        out_pats[patt][element] = (double) val7; element++;
        out_pats[patt][element] = (double) val8; element++;
}

/* printf("\n Closing the input file now. "); */

fclose(infile);
return(0);
}

/*********************
** initialize_net() **
**********************
** Do all the initialization stuff before beginning
*/
static int initialize_net()
{
int err_code;

randomize_wts();
zero_changes();
err_code = read_data_file();
iteration_count = 1;
return(err_code);
}

/**********************
** display_mid_wts() **
***********************
** Display the weights on the middle layer neurodes
** NOTE: This routine is not used in the benchmark
**  test -- RG
**/
/* static void display_mid_wts()
{
int             neurode, weight, row, col;

fprintf(outfile,"\n Weights of Middle Layer neurodes:");

for (neurode=0; neurode<MID_SIZE; neurode++)
{
        fprintf(outfile,"\n  Mid Neurode # %d",neurode);
        for (row=0; row<IN_Y_SIZE; row++)
        {
                fprintf(outfile,"\n ");
                for (col=0; col<IN_X_SIZE; col++)
                {
                        weight = IN_X_SIZE * row + col;
                        fprintf(outfile," %8.3f ", mid_wts[neurode][weight]);
                }
        }
}
return;
}
*/
/**********************
** display_out_wts() **
***********************
** Display the weights on the output layer neurodes
** NOTE: This code is not used in the benchmark
**  test -- RG
*/
/* void  display_out_wts()
{
int             neurode, weight;

        fprintf(outfile,"\n Weights of Output Layer neurodes:");

        for (neurode=0; neurode<OUT_SIZE; neurode++)
        {
                fprintf(outfile,"\n  Out Neurode # %d \n",neurode);
                for (weight=0; weight<MID_SIZE; weight++)
                {
                        fprintf(outfile," %8.3f ", out_wts[neurode][weight]);
                }
        }
        return;
}
*/

/***********************
**  LU DECOMPOSITION  **
** (Linear Equations) **
************************
** These routines come from "Numerical Recipes in Pascal".
** Note that, as in the assignment algorithm, though we
** separately define LUARRAYROWS and LUARRAYCOLS, the two
** must be the same value (this routine depends on a square
** matrix).
*/

/*********
** DoLU **
**********
** Perform the LU decomposition benchmark.
*/
void DoLU(void)
{
LUStruct *loclustruct;  /* Local pointer to global data */
char *errorcontext;
int systemerror;
double *a;
double *b;
double *abase;
double *bbase;
LUdblptr ptra;
int n;
int i;
ulong accumtime;
double iterations;

/*
** Link to global data
*/
loclustruct=&global_lustruct;

/*
** Set error context.
*/
errorcontext="FPU:LU";

/*
** Our first step is to build a "solvable" problem.  This
** will become the "seed" set that all others will be
** derived from. (I.E., we'll simply copy these arrays
** into the others.
*/
a=(double *)AllocateMemory(sizeof(double) * LUARRAYCOLS * LUARRAYROWS,
                &systemerror);
b=(double *)AllocateMemory(sizeof(double) * LUARRAYROWS,
                &systemerror);
n=LUARRAYROWS;

/*
** Build a problem to be solved.
*/
ptra.ptrs.p=a;                  /* Gotta coerce linear array to 2D array */
build_problem(*ptra.ptrs.ap,n,b);

/*
** Now that we have a problem built, see if we need to do
** auto-adjust.  If so, repeatedly call the DoLUIteration routine,
** increasing the number of solutions per iteration as you go.
*/
if(loclustruct->adjust==0)
{
        loclustruct->numarrays=0;
        for(i=1;i<=MAXLUARRAYS;i++)
        {
                abase=(double *)AllocateMemory(sizeof(double) *
                        LUARRAYCOLS*LUARRAYROWS*(i+1),&systemerror);
                if(systemerror)
                {       ReportError(errorcontext,systemerror);
                        LUFreeMem(a,b,(double *)NULL,(double *)NULL);
                        ErrorExit();
                }
                bbase=(double *)AllocateMemory(sizeof(double) *
                        LUARRAYROWS*(i+1),&systemerror);
                if(systemerror)
                {       ReportError(errorcontext,systemerror);
                        LUFreeMem(a,b,abase,(double *)NULL);
                        ErrorExit();
                }       
                if(DoLUIteration(a,b,abase,bbase,i)>global_min_ticks)
                {       loclustruct->numarrays=i;
                        break;
                }
				/*
				** Not enough arrays...free them all and try again
				*/
				FreeMemory((farvoid *)abase,&systemerror);
				FreeMemory((farvoid *)bbase,&systemerror);
        }
        /*
        ** Were we able to do it?
        */
        if(loclustruct->numarrays==0)
        {       printf("FPU:LU -- Array limit reached\n");
                LUFreeMem(a,b,abase,bbase);
                ErrorExit();
        }
}
else
{       /*
        ** Don't need to adjust -- just allocate the proper
        ** number of arrays and proceed.
        */
        abase=(double *)AllocateMemory(sizeof(double) *
                LUARRAYCOLS*LUARRAYROWS*loclustruct->numarrays,
                &systemerror);
        if(systemerror)
        {       ReportError(errorcontext,systemerror);
                LUFreeMem(a,b,(double *)NULL,(double *)NULL);
                ErrorExit();
        }
        bbase=(double *)AllocateMemory(sizeof(double) *
                LUARRAYROWS*loclustruct->numarrays,&systemerror);
        if(systemerror)
        {
                ReportError(errorcontext,systemerror);
                LUFreeMem(a,b,abase,(double *)NULL);
                ErrorExit();
        }
}
/*
** All's well if we get here.  Do the test.
*/
accumtime=0L;
iterations=(double)0.0;

do {
        accumtime+=DoLUIteration(a,b,abase,bbase,
                loclustruct->numarrays);
        iterations+=(double)loclustruct->numarrays;
} while(TicksToSecs(accumtime)<loclustruct->request_secs);

/*
** Clean up, calculate results, and go home.  Be sure to
** show that we don't have to rerun adjustment code.
*/
loclustruct->iterspersec=iterations / TicksToFracSecs(accumtime);

if(loclustruct->adjust==0)
        loclustruct->adjust=1;

LUFreeMem(a,b,abase,bbase);
return;
}

/**************
** LUFreeMem **
***************
** Release memory associated with LU benchmark.
*/
static void LUFreeMem(double *a, double *b,
                        double *abase,double *bbase)
{
int systemerror;

FreeMemory((farvoid *)a,&systemerror);
FreeMemory((farvoid *)b,&systemerror);

if(abase!=(double *)NULL) FreeMemory((farvoid *)abase,&systemerror);
if(bbase!=(double *)NULL) FreeMemory((farvoid *)bbase,&systemerror);
return;
}

/******************
** DoLUIteration **
*******************
** Perform an iteration of the LU decomposition benchmark.
** An iteration refers to the repeated solution of several
** identical matrices.
*/
static ulong DoLUIteration(double *a,double *b,
                double *abase, double *bbase,
                ulong numarrays)
{
double *locabase;
double *locbbase;
LUdblptr ptra;  /* For converting ptr to 2D array */
ulong elapsed;
ulong j,i;              /* Indexes */


/*
** Move the seed arrays (a & b) into the destination
** arrays;
*/
for(j=0;j<numarrays;j++)
{       locabase=abase+j*LUARRAYROWS*LUARRAYCOLS;
        locbbase=bbase+j*LUARRAYROWS;
        for(i=0;i<LUARRAYROWS*LUARRAYCOLS;i++)
                *(locabase+i)=*(a+i);
        for(i=0;i<LUARRAYROWS;i++)
                *(locbbase+i)=*(b+i);
}

/*
** Do test...begin timing.
*/
elapsed=StartStopwatch();
for(i=0;i<numarrays;i++)
{       locabase=abase+i*LUARRAYROWS*LUARRAYCOLS;
        locbbase=bbase+i*LUARRAYROWS;
        ptra.ptrs.p=locabase;
        lusolve(*ptra.ptrs.ap,LUARRAYROWS,locbbase);
}

return(StopStopwatch(elapsed));
}

/******************
** build_problem **
*******************
** Constructs a solvable set of linear equations.  It does this by
** creating an identity matrix, then loading the solution vector
** with random numbers.  After that, the identity matrix and
** solution vector are randomly "scrambled".  Scrambling is
** done by (a) randomly selecting a row and multiplying that
** row by a random number and (b) adding one randomly-selected
** row to another.
*/
static void build_problem(double a[][LUARRAYCOLS],
                int n,
                double b[LUARRAYROWS])
{
long i,j,k,k1;  /* Indexes */
double rcon;     /* Random constant */

/*
** Reset random number generator
*/
randnum(13L);

/*
** Build an identity matrix.
** We'll also use this as a chance to load the solution
** vector.
*/
for(i=0;i<n;i++)
{       b[i]=(double)(abs_randwc(100L)+1L);
        for(j=0;j<n;j++)
                if(i==j)
                        a[i][j]=(double)(abs_randwc(1000L)+1L);
                else
                        a[i][j]=(double)0.0;
}

#ifdef DEBUG
for(i=0;i<n;i++)
{
        for(j=0;j<n;j++)
                printf("%6.2f ",a[i][j]);
        printf(":: %6.2f\n",b[i]);
}
#endif

/*
** Scramble.  Do this 8n times.  See comment above for
** a description of the scrambling process.
*/

for(i=0;i<8*n;i++)
{
        /*
        ** Pick a row and a random constant.  Multiply
        ** all elements in the row by the constant.
        */
 /*       k=abs_randwc((long)n);
        rcon=(double)(abs_randwc(20L)+1L);
        for(j=0;j<n;j++)
                a[k][j]=a[k][j]*rcon;
        b[k]=b[k]*rcon;
*/
        /*
        ** Pick two random rows and add second to
        ** first.  Note that we also occasionally multiply
        ** by minus 1 so that we get a subtraction operation.
        */
        k=abs_randwc((long)n);
        k1=abs_randwc((long)n);
        if(k!=k1)
        {
                if(k<k1) rcon=(double)1.0;
                        else rcon=(double)-1.0;
                for(j=0;j<n;j++)
                        a[k][j]+=a[k1][j]*rcon;;
                b[k]+=b[k1]*rcon;
        }
}

return;
}


/***********
** ludcmp **
************
** From the procedure of the same name in "Numerical Recipes in Pascal",
** by Press, Flannery, Tukolsky, and Vetterling.
** Given an nxn matrix a[], this routine replaces it by the LU
** decomposition of a rowwise permutation of itself.  a[] and n
** are input.  a[] is output, modified as follows:
**   --                       --
**  |  b(1,1) b(1,2) b(1,3)...  |
**  |  a(2,1) b(2,2) b(2,3)...  |
**  |  a(3,1) a(3,2) b(3,3)...  |
**  |  a(4,1) a(4,2) a(4,3)...  |
**  |  ...                      |
**   --                        --
**
** Where the b(i,j) elements form the upper triangular matrix of the
** LU decomposition, and the a(i,j) elements form the lower triangular
** elements.  The LU decomposition is calculated so that we don't
** need to store the a(i,i) elements (which would have laid along the
** diagonal and would have all been 1).
**
** indx[] is an output vector that records the row permutation
** effected by the partial pivoting; d is output as +/-1 depending
** on whether the number of row interchanges was even or odd,
** respectively.
** Returns 0 if matrix singular, else returns 1.
*/
static int ludcmp(double a[][LUARRAYCOLS],
                int n,
                int indx[],
                int *d)
{

double *vv;     /* Row scaling vector */
double big;     /* Holds largest element value */
double sum;
double dum;     /* Holds dummy value */
int i,j,k;      /* Indexes */
int imax;       /* Holds max index value */
double tiny;    /* A really small number */
int systemerror;

tiny=(double)1.0e-20;

/*
** Allocate space for the scaling vector vv.
*/
vv=(double *)AllocateMemory(sizeof(double)*LUARRAYROWS,&systemerror);
if(systemerror)
{       /* If we die here...it's a real mess.... */
        printf("FPU:LU -- Out of memory in ludcmp\n");
        ErrorExit();
}

*d=1;           /* No interchanges yet */

for(i=0;i<n;i++)
{       big=(double)0.0;
        for(j=0;j<n;j++)
                if((double)fabs(a[i][j]) > big)
                        big=fabs(a[i][j]);
        /* Bail out on singular matrix */
        if(big==(double)0.0) return(0);
        vv[i]=1.0/big;
}

/*
** Crout's algorithm...loop over columns.
*/
for(j=0;j<n;j++)
{       if(j!=0)
                for(i=0;i<j;i++)
                {       sum=a[i][j];
                        if(i!=0)
                                for(k=0;k<i;k++)
                                        sum-=(a[i][k]*a[k][j]);
                        a[i][j]=sum;
                }
        big=(double)0.0;
        for(i=j;i<n;i++)
        {       sum=a[i][j];
                if(j!=0)
                        for(k=0;k<j;k++)
                                sum-=a[i][k]*a[k][j];
                a[i][j]=sum;
                dum=vv[i]*fabs(sum);
                if(dum>=big)
                {       big=dum;
                        imax=i;
                }
        }
        if(j!=imax)             /* Interchange rows if necessary */
        {       for(k=0;k<n;k++)
                {       dum=a[imax][k];
                        a[imax][k]=a[j][k];
                        a[j][k]=dum;
                }
                *d=-*d;         /* Change parity of d */
                dum=vv[imax];
                vv[imax]=vv[j]; /* Don't forget scale factor */
                vv[j]=dum;
        }
        indx[j]=imax;
        /*
        ** If the pivot element is zero, the matrix is singular
        ** (at least as far as the precision of the machine
        ** is concerned.)  We'll take the original author's
        ** recommendation and replace 0.0 with "tiny".
        */
        if(a[j][j]==(double)0.0)
                a[j][j]=tiny;

        if(j!=(n-1))
        {       dum=1.0/a[j][j];
                for(i=j+1;i<n;i++)
                        a[i][j]=a[i][j]*dum;
        }
}
FreeMemory((farvoid *)vv,&systemerror);

return(1);
}

/***********
** lubksb **
************
** Also from "Numerical Recipes in Pascal".
** This routine solves the set of n linear equations A X = B.
** Here, a[][] is input, not as the matrix A, but as its
** LU decomposition, created by the routine ludcmp().
** Indx[] is input as the permutation vector returned by ludcmp().
**  b[] is input as the right-hand side an returns the
** solution vector X.
** a[], n, and indx are not modified by this routine and
** can be left in place for different values of b[].
** This routine takes into account the possibility that b will
** begin with many zero elements, so it is efficient for use in
** matrix inversion.
*/
static void lubksb( double a[][LUARRAYCOLS],
                int n,
                int indx[LUARRAYROWS],
                double b[LUARRAYROWS])
{

int i,j;        /* Indexes */
int ip;         /* "pointer" into indx */
int ii;
double sum;

/*
** When ii is set to a positive value, it will become
** the index of the first nonvanishing element of b[].
** We now do the forward substitution. The only wrinkle
** is to unscramble the permutation as we go.
*/
ii=-1;
for(i=0;i<n;i++)
{       ip=indx[i];
        sum=b[ip];
        b[ip]=b[i];
        if(ii!=-1)
                for(j=ii;j<i;j++)
                        sum=sum-a[i][j]*b[j];
        else
                /*
                ** If a nonzero element is encountered, we have
                ** to do the sums in the loop above.
                */
                if(sum!=(double)0.0)
                        ii=i;
        b[i]=sum;
}
/*
** Do backsubstitution
*/
for(i=(n-1);i>=0;i--)
{
        sum=b[i];
        if(i!=(n-1))
                for(j=(i+1);j<n;j++)
                        sum=sum-a[i][j]*b[j];
        b[i]=sum/a[i][i];
}
return;
}

/************
** lusolve **
*************
** Solve a linear set of equations: A x = b
** Original matrix A will be destroyed by this operation.
** Returns 0 if matrix is singular, 1 otherwise.
*/
static int lusolve(double a[][LUARRAYCOLS],
                int n,
                double b[LUARRAYROWS])
{
int indx[LUARRAYROWS];
int d;
#ifdef DEBUG
int i,j;
#endif

if(ludcmp(a,n,indx,&d)==0) return(0);

/* Matrix not singular -- proceed */
lubksb(a,n,indx,b);

#ifdef DEBUG
for(i=0;i<n;i++)
{
        for(j=0;j<n;j++)
                printf("%6.2f ",a[i][j]);
        printf(":: %6.2f\n",b[i]);
}
#endif

return(1);
}
