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

numseq.c

A program to solve and test the problem:
Given an array of integers, find the sequence of elements in that array
that will give the largest sum.

Compiled and tested with gcc under the redhat 5.1 distribution of Linux.

***************************************************************************/
#include <stdio.h>

#define	FALSE		(0)
#define TRUE		(!FALSE)

typedef struct seqstruct
  {
  int	*seqary;	// array of integers
  int	 seqst;		// index of start of maximum sequence
  int	 seqend;	// size of array on input, end of max sequence on output.
  int	 seqsum;	// sum of maximum sequence
  } seqstruct;


/*@begin_fn_hdr@=========================================================
 Name:		numseq
 Synopsis:	find the sequence of ints in an array that gives the largest sum
 Input:		seqstruct  *seqinfo;	// pointer to array and info
 		  seqinfo->seqary;	// pointer to array
 	          seqinfo->seqst;	// ignored on entry, return start of best sequence
		  seqinfo->seqend;	// size of array on entry, return end of best
		  seqinfo->seqsum;	// sum of best sequence

 Output:	return 0 for success, nonzero for failure.
 		modify the value of the seqinfo input parameter to return values.

 Description:
 Do in one pass through the array.	
 While the sign of the array elements does not change, 
   keep collecting the sum for that sign.
 When the sign changes, 
   if we are not in the middle of an ongoing sequence, 
     if the old sign was negative
       note the index of the first positive number as a possible new sequence start.
     if the old sign was positive
       if it is worth bridging the sequence of negative numbers
			       // is the sum of negative numbers smaller than both the old sum
			       // and the next sequence of positive numbers?
         add the sum of the collections of the following neg and pos sequences and continue
       else
         if the sum is better than the best sum so far
	   note the details of the sum
	 continue looking for a better sum.

 Testing:	The test sequences should include:
 		  0's, 
		  0, pos and neg at start
		  singles and multiples of negative and positive numbers, 
		  negatives that are both bigger and smaller than each or 
		  both of the surrounding positive sequences.
		  Only negative numbers (will need to find the best single number)
		  Only positvie numbers
		  All zeros
 Warnings:	Modifying struct passed to it.
 		Mostly inline code, efficient running, uses memory.
		currend may be superfluous

 Updates:	10/13/98 LRC	original
===========================================================@end_fn_hdr@*/


int numseq (seqstruct *seqinfo)
{

int		*val;				// array of values to find best sequence in
int		size;				// size of array
int		currst;				// start of current sequence being tested
int		currend;			// end of current sequence being tested
int		dbend;				// don't bridge end
int		currsum;			// sum of current sequence being tested
int		possum;				// sum of positive bridge test sequence
int		negsum;				// sum of negative bridge test sequence
int		idx;				// index of array element
int		tmp;				// temporary variable

						// flags
int		currpos;			// is the current val/sequence positive
int		lastpos;			// was the last val/sequence positive
int		onlyneg;			// have we only had negative numbers so far
int		seqinprog;			// Is a sequence worth testing in progress
int		bridge;				// Is it worth bridging a seq of neg numbers

val             = seqinfo->seqary;
size            = seqinfo->seqend;
						// Test input values
if (size <=0)					// if it's an illegal size
  return -1;					// return an error

  						// Initialize
seqinfo->seqst  = currst  = 0;			// initial start of best seq
seqinfo->seqend = currend = 0;			// initial end of best seq
seqinfo->seqsum = currsum = *val;		// this will be a sum
currpos         = *val >= 0;			// is the start of the array positive; 
onlyneg         = !currpos;			// have we only had negative numbers so far
seqinprog       =  currpos;			// only start a sequence with positive num

for (val++, idx = 1; idx < size; idx++, val++)	// test every element
  {
  lastpos = currpos;				// was the last number (seq) positive?
   					
  if (onlyneg && (*val > seqinfo->seqsum))	// If we've only had negative numbers. 
    {						// Best yet of single numbers.       
    seqinfo->seqst  = currst  = idx;
    seqinfo->seqend = currend = idx;
    seqinfo->seqsum = currsum = *val;
    onlyneg &= (*val >= 0);			// may have gone positive
    }

  if (! (*val))					// if current element == 0
    continue;					// no change, look at next number

  currpos = (*val > 0);
  if ((currpos != lastpos))			// sign change, end of gathering
    {
    if (!seqinprog) 		
      {
      if (currpos)				// just went pos, start a new sequence		         
        {					
        currst    = idx; 
        currend   = idx; 
        currsum   = *val;
        seqinprog = TRUE;
        }
      continue;					// not at end of sequence, look at next num
      }
 
    						///////////////////////////////
    						//
    						// End of sequence
    						//
    						///////////////////////////////
 
    if (!currpos)				// if goes neg, see if we need to bridge
      {
      bridge = TRUE;				// Assume that we will
      dbend  = idx - 1;				// end if we don't bridge
 
      while ((bridge == TRUE) && (idx < size))
        {
						// While there are still numbers in array
    						// check couplets of pos and neg seqs
 
         negsum = 0;				// gather neg nums and 0's
         for ( ;((idx < size) && (*val <= 0)); idx++, val++)
           {
           negsum += *val;
           }
 
         possum = 0;				// gather pos nums and 0's
        if (idx < size)
          {
           for ( ;((idx < size) && (*val >= 0)); idx++, val++)
             {
             possum += *val;
             }
          }
 
        tmp = (negsum + possum);
        if ((tmp > 0) && (currsum + negsum > 0))	// bridge this couplet?
          {
          currsum += tmp;			// yes
	  currend  = idx -1;
          }
        else
          {
	  currend   = dbend;
          bridge    = FALSE;			// no longer beneficial to bridge
          seqinprog = FALSE;
          }
        }					// end while
      }						// end if(!currpos)
 
    if (currsum > seqinfo->seqsum)		// Do we have the best seq so far?
      {
      seqinfo->seqst  = currst;
      seqinfo->seqend = currend;
      seqinfo->seqsum = currsum;
      }
    }						// end sign change					
  else 						// no sign change, continue with sum
    {
    currsum += *val;
    currend = idx;
    continue;
    }
  }						// end for each value in array
  if (currsum > seqinfo->seqsum)		// Do we have the best seq so far?
    {
    seqinfo->seqst  = currst;
    seqinfo->seqend = currend;
    seqinfo->seqsum = currsum;
    }
  return 0;
}						// end numseq()

/*@begin_fn_hdr@=========================================================
 Name:		main
 Synopsis:	test numseq function
 Input:		none
 Output:	print output of testcases
 Description:	call numseq with various testcases
 Testing:	
 Warnings:	Could also call each test case with any substring
 		first value of test case is number of entries	
 Updates:	10/13/98 LRC	original
===========================================================@end_fn_hdr@*/

void main (void)
{
int		tc0[]= {0};
int		tc1[]= {2, 1, 0};
int		tc2[]= {2, 1, -1};
int		tc3[]= {2, 1, 1};
		      	//	    80, -83,          23,        -12,     36 
int		tc4[]= {13, 12, 23, 45, -83, 2, 4, 5, 12, -2, -7, -3, 17, 19};
		      	// 		   -74,     60, -6  5
int		tc5[]= {9,-23, -14, -30, 0, -7, 23, 37, -6, 5};
int		tc6[]= {5, 0, 0, 0, 0, 0};
		      	//	       15
int		tc7[]= {-5, 1, 2, 3, 4, 5};
			//
int		tc8[]= {3, -23, -14, -30};
			//  1  -1     4          -9          16  -5
int		tc9[]= {12, 1, -1, 2, 2, -3, -3, -3, 4, 4, 4, 4, -5};
int		tc10[]={5, 12, -2, 1, -2, 12};
int	        *testcases[] = {tc0, tc1, tc2, tc3, tc4, tc5, tc6, tc7, tc8, tc9, tc10 };

int		tcnum;
int		tcidx;
int		tcsize;
int 		err;

int		*testseq;
seqstruct	tstruct;

for (tcnum = 0; tcnum < ((sizeof(testcases))/(sizeof(int *))); tcnum++)
  {
  tstruct.seqary = &(testcases[tcnum][1]);
  tstruct.seqend = tcsize = testcases[tcnum][0];
  printf("Test sequence %03d:\n", tcnum);
  printf("%03d elements.  ", tcsize);
  if (tcsize > 0)
    {
    for (tcidx = 1; tcidx <= tcsize; tcidx++)
      {
      printf("%03d, ", testcases[tcnum][tcidx]);
      if (!(tcidx%20))
        {
	printf("\n");
	}
      }						// end for each elem in testcase
    }
  printf("\n");
  err = numseq(&tstruct);
  if (err)
    {
    printf("Invalid.\n");
    }
  else
    {
    printf("Best sequence starts at %03d, ends at %03d, and has the sum %03d\n",
    	    tstruct.seqst, tstruct.seqend, tstruct.seqsum);
    }
  }						// end for each testcase


}
