
/*
 * 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 <stdlib.h>
#include <string.h>
#include <math.h>
#include "stats.h"

struct basic_func
{
    char *cmd;
    unsigned short in_all;
    unsigned short in_help;
    void (*func) ();
};

struct basic_func func_list [] = { { "all",      0, 1, basic_all },
				   { "n",        1, 1, basic_n },
				   { "sum",      1, 1, basic_sum },
				   { "min",      1, 1, basic_min },
				   { "max",      1, 1, basic_max },
				   { "range",    1, 1, basic_range },
                                   { "p1",       1, 1, basic_p1 },
                                   { "p5",       1, 1, basic_p5 },
				   { "p25",      1, 1, basic_p25 },
				   { "p75",      1, 1, basic_p75 },
                                   { "p95",      1, 1, basic_p95 },
                                   { "p99",      1, 1, basic_p99 },
				   { "iqr",      1, 1, basic_iqr },
				   { "mean",     1, 1, basic_mean },
				   { "median",   1, 1, basic_median },
				   { "meand",    1, 1, basic_meand },
				   { "mad",      1, 1, basic_mad },
				   { "variance", 1, 1, basic_variance },
				   { "stddev",   1, 1, basic_standd },
                                   { "standd",   0, 0, basic_standd },
                                   { "cv",       1, 1, basic_cv },
				   { "gmean",    1, 1, basic_gmean },
				   { "fairness", 1, 1, basic_fairness } };

unsigned short basic_labels = TRUE;
unsigned short basic_oneline = FALSE;


unsigned short num_basic_funcs ()
{
    unsigned short list_size;

    list_size = sizeof (func_list);
    if (list_size == 0)
	return (0);
    return (list_size / sizeof (struct basic_func));
}


void basic_usage ()
{
    unsigned short num_funcs = num_basic_funcs ();
    unsigned short counter = 7;
    unsigned short i;

    fprintf (stdout,"usage: basic [variable_list] (space seperated)\n");
    fprintf (stdout," variables:\n  ");
    for (i = 0; i < num_funcs; i++)
    {
        if (!func_list [i].in_help)
            continue;
	fprintf (stdout,"%s%s", func_list [i].cmd, 
		 ((i+ 1) == num_funcs) ? "" : ", ");
	counter += (strlen (func_list [i].cmd) + 2);
	if (counter > 70)
	{
	    fprintf (stdout,"\n  ");
	    counter = 2;
	}
    }
    if (script_file)
	exit (1);
}


void print_label (lab)
char *lab;
{
    if (basic_labels && !basic_oneline)
	fprintf (out,"%20s ", lab);
    else if (basic_labels)
	fprintf (out,"%s ", lab);
}


char basic_delim ()
{
    return (basic_oneline ? ' ' : '\n');
}


void basic_sum ()
{
    print_label ("sum");
    fprintf (out,"%f%c", sum, basic_delim ());
}


double calc_mean ()
{
    return (sum / (double)num_samples);
}


void basic_mean ()
{
    print_label ("mean");
    fprintf (out,"%f%c", calc_mean (), basic_delim ());
}


void basic_min ()
{
    print_label ("minimum");
    fprintf (out,"%f%c", sample [0], basic_delim ());
}


void basic_max ()
{
    print_label ("maximum");
    fprintf (out,"%f%c", sample [num_samples - 1], basic_delim ());
}


void basic_range ()
{
    print_label ("range");
    fprintf (out,"%f%c", sample [num_samples - 1] - sample [0], 
	     basic_delim ());
}


void basic_iqr ()
{
    print_label ("iq range");
    fprintf (out,"%f%c", find_perc (75) - find_perc (25), basic_delim ());
}


void basic_p1 ()
{
    print_label ("1st perc");
    fprintf (out,"%f%c", find_perc (1), basic_delim ());
}


void basic_p5 ()
{
    print_label ("5th perc");
    fprintf (out,"%f%c", find_perc (5), basic_delim ());
}


void basic_p25 ()
{
    print_label ("25th perc");
    fprintf (out,"%f%c", find_perc (25), basic_delim ());
}


void basic_p75 ()
{
    print_label ("75th perc");
    fprintf (out,"%f%c", find_perc (75), basic_delim ());
}


void basic_p95 ()
{
    print_label ("95th perc");
    fprintf (out,"%f%c", find_perc (95), basic_delim ());
}


void basic_p99 ()
{
    print_label ("99th perc");
    fprintf (out,"%f%c", find_perc (99), basic_delim ());
}


void basic_n ()
{
    print_label ("N");
    fprintf (out,"%ld%c", num_samples, basic_delim ());
}


double calc_median ()
{
    double med;

    if ((num_samples % 2) == 1)
	med = sample [(int)(num_samples / 2)];
    else
	med = (sample [(int)(num_samples / 2)] + 
	       sample [(int)((num_samples / 2) - 1)]) / 2;
    return (med);
}


void basic_median ()
{
    double med;

    med = calc_median ();
    print_label ("median");
    fprintf (out,"%f%c", med, basic_delim ());
}


void basic_meand ()
{
    unsigned long i;
    static unsigned long flag = 0;
    static double meand = 0;
    double mean;

    if (data_changed > flag)
    {
	meand = 0;
	mean = sum / (double)num_samples;
	for (i = 0; i < num_samples; i++)
	    meand += fabs (sample [i] - mean);
	meand /= (double)num_samples;
	flag = data_changed;
    }
    print_label ("mean deviation");
    fprintf (out,"%f%c", meand, basic_delim ());
}


/* median absolute deviation */

void basic_mad ()
{
    static double mad = 0;
    static unsigned long flag = 0;
    static unsigned short undef = TRUE;
    unsigned long i;
    double median;
    double *list;

    if (data_changed > flag)
    {
	median = calc_median ();
	if ((list = (double *)malloc (sizeof (double) * num_samples)) == NULL)
	{
	    fprintf (stderr,"out of memory, cannot calculate MAD\n");
	    undef = TRUE;
	}
	else
	{
	    for (i = 0; i < num_samples; i++)
		list [i] = fabs (median - sample [i]);
	    quick_sort (list,0,num_samples - 1);
	    if ((num_samples % 2) == 1)
		mad = list [(int)(num_samples / 2)];
	    else
		mad = (list [(int)(num_samples / 2)] + 
		       list [(int)((num_samples / 2) - 1)]) / 2;
	    undef = FALSE;
	}
    }
    print_label ("mad");
    if (undef)
	fprintf (out,"undefined%c", basic_delim ());
    else
	fprintf (out,"%f%c", mad, basic_delim ());
}


double calc_variance ()
{
    double s2;

    s2 = (1.0 / ((double)num_samples - 1.0)) * 
	(sum_sq - ((sum * sum) / (double)num_samples));
    return (s2);
}


void basic_variance ()
{
    print_label ("variance");
    fprintf (out,"%f%c", calc_variance (), basic_delim ());
}


double calc_standd ()
{
    return (sqrt (calc_variance ()));
}


void basic_standd ()
{
    print_label ("standard deviation");
    fprintf (out,"%f%c", calc_standd (), basic_delim ());
}


void basic_gmean ()
{
    double gmean;

    gmean = pow (prod,(1.0 / (double)num_samples));
    print_label ("geometric mean");
    fprintf (out,"%f%c", gmean, basic_delim ());
}


void basic_fairness ()
{
    double f;

    f = (sum * sum) / ((double)num_samples * sum_sq);
    print_label ("fairness index");
    fprintf (out,"%f%c", f, basic_delim ());
}


/* Metric contributed by Wes Eddy (weddy@grc.nasa.gov), most of his code
   has been replaced (and, so is probably now wrong!). */

void basic_cv ()
{
    print_label ("coeff. of variation");
    fprintf (out, "%f%c", calc_standd () / calc_mean (), basic_delim ());
}


void basic_all ()
{
    unsigned short num_funcs = num_basic_funcs ();
    unsigned short i;

    for (i = 0; i < num_funcs; i++)
        if (func_list [i].in_all)
            func_list [i].func ();
}


void cmd_basic (line,delim)
char *line, *delim;
{
    unsigned short num_funcs = num_basic_funcs ();
    unsigned short i;
    char *var = NULL;

    if (num_samples == 0)
    {
	fprintf (stderr,"no samples on which to run statistics\n");
	if (script_file)
	    exit (1);
	return;
    }
    if (interactive)
	fprintf (out,"\n");
    if ((var = strtok (line,delim)) == NULL)
	basic_all ();
    while (var != NULL)
    {
	for (i = 0; i < num_funcs; i++)
	    if (!strcmp (var, func_list [i].cmd))
	    {
		func_list [i].func ();
		break;
	    }
	if (i == num_funcs)
	    basic_usage ();
	var = strtok (NULL,delim);
    }
    if (basic_oneline)
	fprintf (out,"\n");
    if (interactive)
	fprintf (out,"\n");
}
