/****************************************************************************** * * Copyright © International Business Machines Corp., 2006, 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * NAME * libstats.c * * DESCRIPTION * Some basic statistical analysis convenience tools. * * * USAGE: * To be included in test cases * * AUTHOR * Darren Hart <dvhltc@us.ibm.com> * * HISTORY * 2006-Oct-17: Initial version by Darren Hart * 2009-Jul-22: Addition of stats_container_append function by Kiran Prakash * * TODO: the save routine for gnuplot plotting should be more modular... * *****************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <math.h> #include <libstats.h> #include <librttest.h> #include "../include/realtime_config.h" #ifndef HAVE_EXP10 # define exp10(x) (exp((x) * log(10))) #endif int save_stats = 0; /* static helper functions */ static int stats_record_compare(const void *a, const void *b) { int ret = 0; stats_record_t *rec_a = (stats_record_t *) a; stats_record_t *rec_b = (stats_record_t *) b; if (rec_a->y < rec_b->y) ret = -1; else if (rec_a->y > rec_b->y) ret = 1; return ret; } /* function implementations */ int stats_container_init(stats_container_t * data, long size) { data->size = size; data->index = -1; data->records = calloc(size, sizeof(stats_record_t)); if (!data->records) return -1; return 0; } int stats_container_append(stats_container_t * data, stats_record_t rec) { int myindex = ++data->index; if (myindex >= data->size) { debug(DBG_ERR, "Number of elements cannot be more than %ld\n", data->size); data->index--; return -1; } data->records[myindex] = rec; return myindex; } int stats_container_resize(stats_container_t * data, long size) { stats_record_t *newrecords = realloc(data->records, size * sizeof(stats_record_t)); if (!newrecords) return -1; data->records = newrecords; if (data->size < size) memset(data->records + data->size, 0, size - data->size); data->size = size; return 0; } int stats_container_free(stats_container_t * data) { free(data->records); return 0; } int stats_sort(stats_container_t * data, enum stats_sort_method method) { /* method not implemented, always ascending on y atm */ qsort(data->records, data->index + 1, sizeof(stats_record_t), stats_record_compare); return 0; } float stats_stddev(stats_container_t * data) { long i; float sd, avg, sum, delta; long n; sd = 0.0; n = data->index + 1; sum = 0.0; /* calculate the mean */ for (i = 0; i < n; i++) { sum += data->records[i].y; } avg = sum / (float)n; /* calculate the standard deviation */ sum = 0.0; for (i = 0; i < n; i++) { delta = (data->records[i].y - avg); sum += delta * delta; } sum /= n; sd = sqrt(sum); return sd; } float stats_avg(stats_container_t * data) { long i; float avg, sum; long n; n = data->index + 1; sum = 0.0; /* calculate the mean */ for (i = 0; i < n; i++) { sum += data->records[i].y; } avg = sum / (float)n; return avg; } long stats_min(stats_container_t * data) { long i; long min; long n; n = data->index + 1; /* calculate the mean */ min = data->records[0].y; for (i = 1; i < n; i++) { if (data->records[i].y < min) { min = data->records[i].y; } } return min; } long stats_max(stats_container_t * data) { long i; long max; long n; n = data->index + 1; /* calculate the mean */ max = data->records[0].y; for (i = 1; i < n; i++) { if (data->records[i].y > max) { max = data->records[i].y; } } return max; } int stats_quantiles_init(stats_quantiles_t * quantiles, int nines) { if (nines < 2) { return -1; } quantiles->nines = nines; /* allocate space for quantiles, starting with 0.99 (two nines) */ quantiles->quantiles = calloc(sizeof(long), (nines - 1)); if (!quantiles->quantiles) { return -1; } return 0; } int stats_quantiles_free(stats_quantiles_t * quantiles) { free(quantiles->quantiles); return 0; } int stats_quantiles_calc(stats_container_t * data, stats_quantiles_t * quantiles) { int i; int size; int index; // check for sufficient data size of accurate calculation if (data->index < 0 || (data->index + 1) < (long)exp10(quantiles->nines)) { return -1; } size = data->index + 1; stats_sort(data, ASCENDING_ON_Y); for (i = 2; i <= quantiles->nines; i++) { index = size - size / exp10(i); quantiles->quantiles[i - 2] = data->records[index].y; } return 0; } void stats_quantiles_print(stats_quantiles_t * quantiles) { int i; int fraction = 0; for (i = 0; i <= quantiles->nines - 2; i++) { if (i > 0) fraction += 9 * exp10(i - 1); printf("99.%d%% < %ld\n", fraction, quantiles->quantiles[i]); } } int stats_hist(stats_container_t * hist, stats_container_t * data) { int i; int ret; long min, max, width; long y, b; ret = 0; if (hist->size <= 0 || data->index < 0) { return -1; } /* calculate the range of dataset */ min = max = data->records[0].y; for (i = 0; i <= data->index; i++) { y = data->records[i].y; if (y > max) max = y; if (y < min) min = y; } /* define the bucket ranges */ width = MAX((max - min) / hist->size, 1); hist->records[0].x = min; for (i = 1; i < (hist->size); i++) { hist->records[i].x = min + i * width; } /* fill in the counts */ for (i = 0; i <= data->index; i++) { y = data->records[i].y; b = MIN((y - min) / width, hist->size - 1); hist->records[b].y++; } return 0; } void stats_hist_print(stats_container_t * hist) { long i, x; for (i = 0; i < hist->size; i++) { x = hist->records[i].x; if (i < hist->size - 1) printf("[%ld,%ld) = %ld\n", x, hist->records[i + 1].x, hist->records[i].y); else printf("[%ld,-) = %ld\n", x, hist->records[i].y); } } int stats_container_save(char *filename, char *title, char *xlabel, char *ylabel, stats_container_t * data, char *mode) { int i; int minx = 0, maxx = 0, miny = 0, maxy = 0; FILE *dat_fd; FILE *plt_fd; char *datfile; char *pltfile; stats_record_t *rec; if (!save_stats) return 0; /* generate the filenames */ if (asprintf(&datfile, "%s.dat", filename) == -1) { fprintf(stderr, "Failed to allocate string for data filename\n"); return -1; } if (asprintf(&pltfile, "%s.plt", filename) == -1) { fprintf(stderr, "Failed to allocate string for plot filename\n"); return -1; } /* generate the data file */ if ((dat_fd = fopen(datfile, "w")) == NULL) { perror("Failed to open dat file"); return -1; } else { minx = maxx = data->records[0].x; miny = maxy = data->records[0].y; for (i = 0; i <= data->index; i++) { rec = &data->records[i]; minx = MIN(minx, rec->x); maxx = MAX(maxx, rec->x); miny = MIN(miny, rec->y); maxy = MAX(maxy, rec->y); fprintf(dat_fd, "%ld %ld\n", rec->x, rec->y); } fclose(dat_fd); } /* generate the plt file */ if (!(plt_fd = fopen(pltfile, "w"))) { perror("Failed to open plt file"); return -1; } else { fprintf(plt_fd, "set terminal png\n"); fprintf(plt_fd, "set output \"%s.png\"\n", pltfile); fprintf(plt_fd, "set title \"%s\"\n", title); fprintf(plt_fd, "set xlabel \"%s\"\n", xlabel); fprintf(plt_fd, "set ylabel \"%s\"\n", ylabel); fprintf(plt_fd, "plot [0:%d] [0:%d] \"%s\" with %s\n", maxx + 1, maxy + 1, datfile, mode); fclose(plt_fd); } return 0; }