Home | History | Annotate | Download | only in lib
      1 /******************************************************************************
      2  *
      3  *   Copyright  International Business Machines  Corp., 2006, 2008
      4  *
      5  *   This program is free software;  you can redistribute it and/or modify
      6  *   it under the terms of the GNU General Public License as published by
      7  *   the Free Software Foundation; either version 2 of the License, or
      8  *   (at your option) any later version.
      9  *
     10  *   This program is distributed in the hope that it will be useful,
     11  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
     12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     13  *   the GNU General Public License for more details.
     14  *
     15  *   You should have received a copy of the GNU General Public License
     16  *   along with this program;  if not, write to the Free Software
     17  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     18  *
     19  * NAME
     20  *	   libstats.c
     21  *
     22  * DESCRIPTION
     23  *	  Some basic statistical analysis convenience tools.
     24  *
     25  *
     26  * USAGE:
     27  *	  To be included in test cases
     28  *
     29  * AUTHOR
     30  *		Darren Hart <dvhltc (at) us.ibm.com>
     31  *
     32  * HISTORY
     33  *	  2006-Oct-17: Initial version by Darren Hart
     34  *	  2009-Jul-22: Addition of stats_container_append function by Kiran Prakash
     35  *
     36  * TODO: the save routine for gnuplot plotting should be more modular...
     37  *
     38  *****************************************************************************/
     39 
     40 #include <stdio.h>
     41 #include <stdlib.h>
     42 #include <string.h>
     43 #include <errno.h>
     44 #include <unistd.h>
     45 #include <math.h>
     46 #include <libstats.h>
     47 #include <librttest.h>
     48 
     49 #include "../include/realtime_config.h"
     50 
     51 #ifndef HAVE_EXP10
     52 # define exp10(x) (exp((x) * log(10)))
     53 #endif
     54 
     55 int save_stats = 0;
     56 
     57 /* static helper functions */
     58 static int stats_record_compare(const void *a, const void *b)
     59 {
     60 	int ret = 0;
     61 	stats_record_t *rec_a = (stats_record_t *) a;
     62 	stats_record_t *rec_b = (stats_record_t *) b;
     63 	if (rec_a->y < rec_b->y)
     64 		ret = -1;
     65 	else if (rec_a->y > rec_b->y)
     66 		ret = 1;
     67 	return ret;
     68 }
     69 
     70 /* function implementations */
     71 int stats_container_init(stats_container_t * data, long size)
     72 {
     73 	data->size = size;
     74 	data->index = -1;
     75 	data->records = calloc(size, sizeof(stats_record_t));
     76 	if (!data->records)
     77 		return -1;
     78 	return 0;
     79 }
     80 
     81 int stats_container_append(stats_container_t * data, stats_record_t rec)
     82 {
     83 	int myindex = ++data->index;
     84 	if (myindex >= data->size) {
     85 		debug(DBG_ERR, "Number of elements cannot be more than %ld\n",
     86 		      data->size);
     87 		data->index--;
     88 		return -1;
     89 	}
     90 	data->records[myindex] = rec;
     91 	return myindex;
     92 }
     93 
     94 int stats_container_resize(stats_container_t * data, long size)
     95 {
     96 	stats_record_t *newrecords =
     97 	    realloc(data->records, size * sizeof(stats_record_t));
     98 	if (!newrecords)
     99 		return -1;
    100 	data->records = newrecords;
    101 	if (data->size < size)
    102 		memset(data->records + data->size, 0, size - data->size);
    103 	data->size = size;
    104 	return 0;
    105 }
    106 
    107 int stats_container_free(stats_container_t * data)
    108 {
    109 	free(data->records);
    110 	return 0;
    111 }
    112 
    113 int stats_sort(stats_container_t * data, enum stats_sort_method method)
    114 {
    115 	/* method not implemented, always ascending on y atm */
    116 	qsort(data->records, data->index + 1, sizeof(stats_record_t),
    117 	      stats_record_compare);
    118 	return 0;
    119 }
    120 
    121 float stats_stddev(stats_container_t * data)
    122 {
    123 	long i;
    124 	float sd, avg, sum, delta;
    125 	long n;
    126 
    127 	sd = 0.0;
    128 	n = data->index + 1;
    129 	sum = 0.0;
    130 
    131 	/* calculate the mean */
    132 	for (i = 0; i < n; i++) {
    133 		sum += data->records[i].y;
    134 	}
    135 	avg = sum / (float)n;
    136 
    137 	/* calculate the standard deviation */
    138 	sum = 0.0;
    139 	for (i = 0; i < n; i++) {
    140 		delta = (data->records[i].y - avg);
    141 		sum += delta * delta;
    142 	}
    143 	sum /= n;
    144 
    145 	sd = sqrt(sum);
    146 
    147 	return sd;
    148 }
    149 
    150 float stats_avg(stats_container_t * data)
    151 {
    152 	long i;
    153 	float avg, sum;
    154 	long n;
    155 
    156 	n = data->index + 1;
    157 	sum = 0.0;
    158 
    159 	/* calculate the mean */
    160 	for (i = 0; i < n; i++) {
    161 		sum += data->records[i].y;
    162 	}
    163 	avg = sum / (float)n;
    164 
    165 	return avg;
    166 }
    167 
    168 long stats_min(stats_container_t * data)
    169 {
    170 	long i;
    171 	long min;
    172 	long n;
    173 
    174 	n = data->index + 1;
    175 
    176 	/* calculate the mean */
    177 	min = data->records[0].y;
    178 	for (i = 1; i < n; i++) {
    179 		if (data->records[i].y < min) {
    180 			min = data->records[i].y;
    181 		}
    182 	}
    183 
    184 	return min;
    185 }
    186 
    187 long stats_max(stats_container_t * data)
    188 {
    189 	long i;
    190 	long max;
    191 	long n;
    192 
    193 	n = data->index + 1;
    194 
    195 	/* calculate the mean */
    196 	max = data->records[0].y;
    197 	for (i = 1; i < n; i++) {
    198 		if (data->records[i].y > max) {
    199 			max = data->records[i].y;
    200 		}
    201 	}
    202 
    203 	return max;
    204 }
    205 
    206 int stats_quantiles_init(stats_quantiles_t * quantiles, int nines)
    207 {
    208 	if (nines < 2) {
    209 		return -1;
    210 	}
    211 	quantiles->nines = nines;
    212 	/* allocate space for quantiles, starting with 0.99 (two nines) */
    213 	quantiles->quantiles = calloc(sizeof(long), (nines - 1));
    214 	if (!quantiles->quantiles) {
    215 		return -1;
    216 	}
    217 	return 0;
    218 }
    219 
    220 int stats_quantiles_free(stats_quantiles_t * quantiles)
    221 {
    222 	free(quantiles->quantiles);
    223 	return 0;
    224 }
    225 
    226 int stats_quantiles_calc(stats_container_t * data,
    227 			 stats_quantiles_t * quantiles)
    228 {
    229 	int i;
    230 	int size;
    231 	int index;
    232 
    233 	// check for sufficient data size of accurate calculation
    234 	if (data->index < 0 ||
    235 	    (data->index + 1) < (long)exp10(quantiles->nines)) {
    236 		return -1;
    237 	}
    238 
    239 	size = data->index + 1;
    240 	stats_sort(data, ASCENDING_ON_Y);
    241 
    242 	for (i = 2; i <= quantiles->nines; i++) {
    243 		index = size - size / exp10(i);
    244 		quantiles->quantiles[i - 2] = data->records[index].y;
    245 	}
    246 	return 0;
    247 }
    248 
    249 void stats_quantiles_print(stats_quantiles_t * quantiles)
    250 {
    251 	int i;
    252 	int fraction = 0;
    253 	for (i = 0; i <= quantiles->nines - 2; i++) {
    254 		if (i > 0)
    255 			fraction += 9 * exp10(i - 1);
    256 		printf("99.%d%% < %ld\n", fraction, quantiles->quantiles[i]);
    257 	}
    258 }
    259 
    260 int stats_hist(stats_container_t * hist, stats_container_t * data)
    261 {
    262 	int i;
    263 	int ret;
    264 	long min, max, width;
    265 	long y, b;
    266 
    267 	ret = 0;
    268 
    269 	if (hist->size <= 0 || data->index < 0) {
    270 		return -1;
    271 	}
    272 
    273 	/* calculate the range of dataset */
    274 	min = max = data->records[0].y;
    275 	for (i = 0; i <= data->index; i++) {
    276 		y = data->records[i].y;
    277 		if (y > max)
    278 			max = y;
    279 		if (y < min)
    280 			min = y;
    281 	}
    282 
    283 	/* define the bucket ranges */
    284 	width = MAX((max - min) / hist->size, 1);
    285 	hist->records[0].x = min;
    286 	for (i = 1; i < (hist->size); i++) {
    287 		hist->records[i].x = min + i * width;
    288 	}
    289 
    290 	/* fill in the counts */
    291 	for (i = 0; i <= data->index; i++) {
    292 		y = data->records[i].y;
    293 		b = MIN((y - min) / width, hist->size - 1);
    294 		hist->records[b].y++;
    295 	}
    296 
    297 	return 0;
    298 }
    299 
    300 void stats_hist_print(stats_container_t * hist)
    301 {
    302 	long i, x;
    303 	for (i = 0; i < hist->size; i++) {
    304 		x = hist->records[i].x;
    305 		if (i < hist->size - 1)
    306 			printf("[%ld,%ld) = %ld\n", x,
    307 			       hist->records[i + 1].x, hist->records[i].y);
    308 		else
    309 			printf("[%ld,-) = %ld\n", x, hist->records[i].y);
    310 	}
    311 }
    312 
    313 int stats_container_save(char *filename, char *title, char *xlabel,
    314 			 char *ylabel, stats_container_t * data, char *mode)
    315 {
    316 	int i;
    317 	int minx = 0, maxx = 0, miny = 0, maxy = 0;
    318 	FILE *dat_fd;
    319 	FILE *plt_fd;
    320 	char *datfile;
    321 	char *pltfile;
    322 	stats_record_t *rec;
    323 
    324 	if (!save_stats)
    325 		return 0;
    326 
    327 	/* generate the filenames */
    328 	if (asprintf(&datfile, "%s.dat", filename) == -1) {
    329 		fprintf(stderr,
    330 			"Failed to allocate string for data filename\n");
    331 		return -1;
    332 	}
    333 	if (asprintf(&pltfile, "%s.plt", filename) == -1) {
    334 		fprintf(stderr,
    335 			"Failed to allocate string for plot filename\n");
    336 		return -1;
    337 	}
    338 
    339 	/* generate the data file */
    340 	if ((dat_fd = fopen(datfile, "w")) == NULL) {
    341 		perror("Failed to open dat file");
    342 		return -1;
    343 	} else {
    344 		minx = maxx = data->records[0].x;
    345 		miny = maxy = data->records[0].y;
    346 		for (i = 0; i <= data->index; i++) {
    347 			rec = &data->records[i];
    348 			minx = MIN(minx, rec->x);
    349 			maxx = MAX(maxx, rec->x);
    350 			miny = MIN(miny, rec->y);
    351 			maxy = MAX(maxy, rec->y);
    352 			fprintf(dat_fd, "%ld %ld\n", rec->x, rec->y);
    353 		}
    354 		fclose(dat_fd);
    355 	}
    356 
    357 	/* generate the plt file */
    358 	if (!(plt_fd = fopen(pltfile, "w"))) {
    359 		perror("Failed to open plt file");
    360 		return -1;
    361 	} else {
    362 		fprintf(plt_fd, "set terminal png\n");
    363 		fprintf(plt_fd, "set output \"%s.png\"\n", pltfile);
    364 		fprintf(plt_fd, "set title \"%s\"\n", title);
    365 		fprintf(plt_fd, "set xlabel \"%s\"\n", xlabel);
    366 		fprintf(plt_fd, "set ylabel \"%s\"\n", ylabel);
    367 		fprintf(plt_fd, "plot [0:%d] [0:%d] \"%s\" with %s\n",
    368 			maxx + 1, maxy + 1, datfile, mode);
    369 		fclose(plt_fd);
    370 	}
    371 
    372 	return 0;
    373 }
    374