
#ifdef TRANSFORM
/*
 * Copyright (c) 2001 BBNT Solutions LLC
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice and this permission
 * appear in all copies and in supporting documentation, and that the
 * name of BBN Technologies not be used in advertising or publicity
 * pertaining to distribution of the software without specific,
 * written prior permission.  BBN makes no representations about the
 * suitability of this software for any purposes.  It is provided "AS
 * IS" without express or implied warranties.
 */

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "stats.h"

double scale_factor = 0;
double incr_factor = 0;
double power_factor = 0;
unsigned long mod_factor = 0;
double ewma_gain = 0;
long window_size = 0;
double (*transform_func)() = NULL;
/* void *transform_func = NULL; */

#define FUNC 100

void transform_usage ()
{
    fprintf (stderr,"\nusage: transform [-d] func\n");
    fprintf (stderr," -d dumps the dataset after transforming\n");
    fprintf (stderr," functions:\n");
    fprintf (stderr,"   average      average of previous samples\n");
    fprintf (stderr,"   ceil         take the ceiling of each sample\n");
    fprintf (stdout,"   div X        divide by given X\n");
    fprintf (stdout,"   floor        take the floor of each sample\n");
    fprintf (stderr,"   ewma G       EWMA of previous samples w/ gain of G\n");
    fprintf (stderr,"   incr X       increae/decrease by given factor\n");
    fprintf (stderr,"   loge         natural log\n");
    fprintf (stderr,"   log10        log base 10\n");
    fprintf (stderr,"   mod X        mod each number by X\n");
    fprintf (stderr,"   power X      raise to the X-th power\n");
    fprintf (stderr,"   relative     normalize all numbers to the first\n");
    fprintf (stderr,"   scale X      scale by given factor\n");
    fprintf (stderr,"   slide-win X  sliding window of size X\n");
    fprintf (stderr,"\n");
}


void do_sliding_win_transform ()
{
    unsigned long i, j;
    double sum = 0;

    total_clear ();
    for (i = 0; i < window_size; i++)
	sum += sample [i];
    sample [0] = sum / window_size;
    for (i = window_size, j = 1; i < num_samples; i++, j++)
    {
	sum -= sample [j];
	sum += sample [i];
	sample [j] = sum / window_size;
	total_add_sample (sample [j]);
    }
    num_samples -= (window_size - 1);
    data_changed++;
}


void do_transform (by)
unsigned short by;
{
    double sum;
    unsigned long i;
    unsigned short base_set = FALSE;
    double base;

    total_clear ();
    for (i = 0; i < num_samples; i++)
    {
	if (transform_func != NULL)
	    sample [i] = (double)transform_func (sample [i]);
	else if (by == SCALE)
	    sample [i] *= scale_factor;
	else if (by == INCR)
	    sample [i] += incr_factor;
	else if (by == POWER)
	    sample [i] = pow (sample [i],power_factor);
        else if (by == MODULO)
            sample [i] = (long)sample [i] % mod_factor;
	else if ((by == AVERAGE) && (i > 0))
	{
	    sum = (double)(sample [i - 1] * i) + sample [i];
	    sample [i] = 0;
	    if (sum > 0)
		sample [i] = sum / (i + 1);
	}
	else if ((by == EWMA) && (i > 0))
	    sample [i] = (sample [i] * ewma_gain) + 
		(sample [i - 1] * (1 - ewma_gain));
	else if (by == RELATIVE)
	{
	    if (!base_set)
	    {
		base = sample [i];
		base_set = TRUE;
	    }
	    sample [i] -= base;
	}
	total_add_sample (sample [i]);
    }
    data_changed++;
}


void cmd_transform ()
{
    char *func, *arg;
    unsigned short transform_by;
    unsigned short transform_dump = FALSE;
    unsigned short bad_opt = FALSE;

    if ((func = strtok (NULL," ")) == NULL)
    {
	transform_usage ();
	if (script_file)
	    exit (1);
	return;
    }
    while (func [0] == '-')
    {
	if (!strcmp (func,"-d"))
	    transform_dump = TRUE;
	else
	    bad_opt = TRUE;
	if (bad_opt || ((func = strtok (NULL," ")) == NULL))
	{
	    transform_usage ();
	    if (script_file)
		exit (1);
	    return;
	}
    }
    if (!strcmp (func,"loge"))
	transform_func = log;
    else if (!strcmp (func,"log10"))
	transform_func = log10;
    else if (!strcmp (func,"ceil"))
	transform_func = ceil;
    else if (!strcmp (func,"floor"))
	transform_func = floor;
    else if (!strcmp (func,"relative"))
	transform_by = RELATIVE;
    else if (!strcmp (func,"average"))
	transform_by = AVERAGE;
    else if (!strcmp (func,"scale") || !strcmp (func,"div"))
    {
	if ((arg = strtok (NULL," ")) == NULL)
	{
	    transform_usage ();
	    if (script_file)
		exit (1);
	    return;
	}
	transform_by = SCALE;
	scale_factor = atof (arg);
	if (!strcmp (func,"div") && (scale_factor != 0))
	    scale_factor = 1.0 / scale_factor;
    }
    else if (!strcmp (func,"mod"))
    {
	if ((arg = strtok (NULL," ")) == NULL)
	{
	    transform_usage ();
	    if (script_file)
		exit (1);
	    return;
	}
	transform_by = MODULO;
	mod_factor = atol (arg);
    }
    else if (!strcmp (func,"incr"))
    {
	if ((arg = strtok (NULL," ")) == NULL)
	{
	    transform_usage ();
	    if (script_file)
		exit (1);
	    return;
	}
	transform_by = INCR;
	incr_factor = atof (arg);
    }
    else if (!strcmp (func,"power"))
    {
	if ((arg = strtok (NULL," ")) == NULL)
	{
	    transform_usage ();
	    if (script_file)
		exit (1);
	    return;
	}
	transform_by = POWER;
	power_factor = atof (arg);
    }
    else if (!strcmp (func,"ewma"))
    {
	if ((arg = strtok (NULL," ")) == NULL)
	{
	    transform_usage ();
	    if (script_file)
		exit (1);
	    return;
	}
	transform_by = EWMA;
	ewma_gain = atof (arg);
	if ((ewma_gain < 0.0) || (ewma_gain > 1.0))
	{
	    fprintf (stderr,"EWMA gain must be between 0.0 and 1.0\n");
	    transform_usage ();
	    if (script_file)
		exit (1);
	    return;
	}
    }
    else if (!strcmp (func,"slide-win"))
    {
	if ((arg = strtok (NULL," ")) == NULL)
	{
	    transform_usage ();
	    if (script_file)
		exit (1);
	    return;
	}
	transform_by = SLIDING_WIN;
	window_size = atol (arg);
	if ((window_size < 0) || (window_size >= num_samples))
	{
	    fprintf (stderr,"window size must be > 0 and <= #of samples\n");
	    transform_usage ();
	    if (script_file)
		exit (1);
	    return;
	}
    }
    else
    {
	transform_usage ();
	if (script_file)
	    exit (1);
	return;
    }
    if (transform_by == SLIDING_WIN)
	do_sliding_win_transform ();
    else
	do_transform (transform_by);
    transform_func = NULL;
    if (transform_dump)
	cmd_dump ();
}

#endif /* TRANSFORM */
