/* globals go before main */
global
#define SAVEINC 100
/* not available before 1.3.0b25 */
#define I __i
/* our save record contains a funcalc row record and a place for raw data */
typedef struct savestruct{
  RowRec row;
  char *raw;
} *Save, SaveRec;

/* qsort comparison routine -- reverse order of x */
static int _Cmp(const void *s1, const void *s2)
{
  Save f1 = (Save)s1;
  Save f2 = (Save)s2;
  /* order flag */
  if( f1->row.x < f2->row.x )
    return -1;
  if( f1->row.x > f2->row.x )
    return 1;
  else
    return 0;
}

/* this is the heart of the algorithm */
static void ProcessRows(Fun fun, Fun ofun, Save save, int nsave)
{
  int i;
  /* sort rows */
  qsort(save, nsave, sizeof(SaveRec), _Cmp);
  /* output rows in new order */
  for(i=0; i<nsave; i++){
    /* re-associate raw data with this row */
    FunInfoPut(fun, FUN_RAWBUF, &save[i].raw, 0);
    /* write the row */
    FunTableRowPut(ofun, &save[i].row, 1, 0, NULL);
  }
}
end

/* locals go immediately after local variables in main */
local
  char *cptr=NULL;
  char *rptr=NULL;
  int i=0;
  int group=0;
  int rawsize=0;
  int lastkey=0;
  int maxsave=0;
  int nsave=0;
  Save save=NULL;
end

/* execute this before we enter inner loop */
before
  /* get size of raw input record */
  FunInfoGet(fun, FUN_RAWSIZE, &rawsize, 0);
end

/* MAIN ROW PROCESSING LOOP */

/* first time through, just seed last key */
if( NROW == 0 ) lastkey=cur->key;

/* save raw data for this set of rows */
FunInfoGet(fun, FUN_RAWBUF, &rptr, 0);

/* if this key starts a new group, process and output previous group */
if( cur->key != lastkey ){
  /* process current group */
  ProcessRows(fun, ofun, save, nsave);
  /* restore the current row pointers so we can process the next row */
  FunInfoPut(fun, FUN_RAWBUF, &rptr, 0);
  /* reset number of keys in this group */
  nsave = 0;
  /* got another group */
  group++;
}

/* user manipulation */
/* set values of a new column */
cur->group:I = group;
/* manipulate one of the user columns */
cur->x = -cur->x;

/* save this row ... first make sure we have enough space */
if( nsave >= maxsave ){
  maxsave += SAVEINC;
  if( !save )
    save = xmalloc(maxsave*sizeof(SaveRec));
  else
    save = xrealloc(save, maxsave*sizeof(SaveRec));
  /* clear what we just alloc'ed (wish realloc() would do this! */
  memset(save+((maxsave-SAVEINC)*sizeof(SaveRec)), 0, SAVEINC*sizeof(SaveRec));
}
/* save the user part of this row */
memcpy(&(save[nsave].row), cur, sizeof(RowRec));
/* might have to allocate space for raw */
if( !save[nsave].raw ) save[nsave].raw = xmalloc(rawsize);
/* save raw data for this row */
memcpy(save[nsave].raw, rptr+(I*rawsize), rawsize);
/* saved another key in this group */
nsave++;

/* this key becomes last key */
lastkey = cur->key;

/* prevent funcalc from writing out the current record */
continue;

/* END OF INNER LOOP */

/* after we leave inner loop */
after
  /* process rows in last group */
  ProcessRows(fun, ofun, save, nsave);
  /* restore raw buf pointer so FunClose can free it properly */
  FunInfoPut(fun, FUN_RAWBUF, &rptr, 0);
  /* clean up saved data */
  for(i=0; i<maxsave; i++){
    if( save[i].raw ) xfree(save[i].raw);
  }
  if( save ) xfree(save);
end
