1 /******************************************************************************
3 * Copyright © International Business Machines Corp., 2006, 2008
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * Some basic statistical analysis convenience tools.
27 * To be included in test cases
30 * Darren Hart <dvhltc@us.ibm.com>
33 * 2006-Oct-17: Initial version by Darren Hart
34 * 2009-Jul-22: Addition of stats_container_append function by Kiran Prakash
36 * TODO: the save routine for gnuplot plotting should be more modular...
38 *****************************************************************************/
47 #include <librttest.h>
51 /* static helper functions */
52 static int stats_record_compare(const void * a
, const void * b
) {
54 stats_record_t
* rec_a
= (stats_record_t
*)a
;
55 stats_record_t
* rec_b
= (stats_record_t
*)b
;
56 if (rec_a
->y
< rec_b
->y
)
58 else if (rec_a
->y
> rec_b
->y
)
63 /* function implementations */
64 int stats_container_init(stats_container_t
*data
, long size
)
68 data
->records
= calloc(size
, sizeof(stats_record_t
));
74 int stats_container_append(stats_container_t
*data
, stats_record_t rec
)
76 int myindex
= ++data
->index
;
77 if (myindex
>= data
->size
) {
78 debug(DBG_ERR
, "Number of elements cannot be more than %ld\n",
83 data
->records
[myindex
] = rec
;
87 int stats_container_resize(stats_container_t
*data
, long size
)
89 stats_record_t
*newrecords
= realloc(data
->records
, size
*sizeof(stats_record_t
));
92 data
->records
= newrecords
;
93 if (data
->size
< size
)
94 memset(data
->records
+ data
->size
, 0, size
- data
->size
);
99 int stats_container_free(stats_container_t
*data
)
105 int stats_sort(stats_container_t
*data
, enum stats_sort_method method
)
107 // method not implemented, always ascending on y atm
108 qsort(data
->records
, data
->index
+ 1, sizeof(stats_record_t
),
109 stats_record_compare
);
113 float stats_stddev(stats_container_t
*data
)
116 float sd
, avg
, sum
, delta
;
123 /* calculate the mean */
124 for (i
= 0; i
< n
; i
++) {
125 sum
+= data
->records
[i
].y
;
127 avg
= sum
/ (float)n
;
129 /* calculate the standard deviation */
131 for (i
= 0; i
< n
; i
++) {
132 delta
= (data
->records
[i
].y
- avg
);
142 float stats_avg(stats_container_t
*data
)
151 /* calculate the mean */
152 for (i
= 0; i
< n
; i
++) {
153 sum
+= data
->records
[i
].y
;
155 avg
= sum
/ (float)n
;
160 long stats_min(stats_container_t
*data
)
168 /* calculate the mean */
169 min
= data
->records
[0].y
;
170 for (i
= 1; i
< n
; i
++) {
171 if (data
->records
[i
].y
< min
) {
172 min
= data
->records
[i
].y
;
179 long stats_max(stats_container_t
*data
)
187 /* calculate the mean */
188 max
= data
->records
[0].y
;
189 for (i
= 1; i
< n
; i
++) {
190 if (data
->records
[i
].y
> max
) {
191 max
= data
->records
[i
].y
;
198 int stats_quantiles_init(stats_quantiles_t
*quantiles
, int nines
)
203 quantiles
->nines
= nines
;
204 // allocate space for quantiles, starting with 0.99 (two nines)
205 quantiles
->quantiles
= malloc(sizeof(long) * (nines
-1));
206 if (!quantiles
->quantiles
) {
209 memset(quantiles
->quantiles
, 0, (nines
-1) * sizeof(long));
213 int stats_quantiles_free(stats_quantiles_t
*quantiles
)
215 free(quantiles
->quantiles
);
219 int stats_quantiles_calc(stats_container_t
*data
, stats_quantiles_t
*quantiles
)
225 // check for sufficient data size of accurate calculation
226 if (data
->index
< 0 || (data
->index
+ 1) \
227 < (long)exp10(quantiles
->nines
)) {
228 //printf("ERROR: insufficient data size for %d nines\n", data->size);
232 size
= data
->index
+ 1;
233 stats_sort(data
, ASCENDING_ON_Y
);
235 for (i
= 2; i
<= quantiles
->nines
; i
++) {
236 index
= size
- size
/ exp10(i
);
237 quantiles
->quantiles
[i
-2] = data
->records
[index
].y
;
242 void stats_quantiles_print(stats_quantiles_t
*quantiles
)
246 for (i
= 0; i
<= quantiles
->nines
-2; i
++)
249 fraction
+= 9 * exp10(i
- 1);
250 printf("99.%d%% < %ld\n", fraction
, quantiles
->quantiles
[i
]);
254 int stats_hist(stats_container_t
*hist
, stats_container_t
*data
)
258 long min
, max
, width
;
263 if (hist
->size
<= 0 || data
->index
< 0) {
267 /* calculate the range of dataset */
268 min
= max
= data
->records
[0].y
;
269 for (i
= 0; i
<= data
->index
; i
++) {
270 y
= data
->records
[i
].y
;
271 if (y
> max
) max
= y
;
272 if (y
< min
) min
= y
;
274 //printf("min = %d\n", min);
275 //printf("max = %d\n", max);
277 /* define the bucket ranges */
278 width
= MAX((max
-min
)/hist
->size
, 1);
279 //printf("width = %d\n", width);
280 hist
->records
[0].x
= min
;
281 //printf("hist[0].x = %d\n", min);
282 for (i
= 1; i
< (hist
->size
); i
++) {
283 hist
->records
[i
].x
= min
+ i
*width
;
284 //printf("hist[%d].x = %d\n", i, hist->records[i].x);
287 /* fill in the counts */
288 for (i
= 0; i
<= data
->index
; i
++) {
289 y
= data
->records
[i
].y
;
290 b
= MIN((y
-min
)/width
, hist
->size
-1);
291 //printf("%d will go in bucket %d\n", y, b);
292 hist
->records
[b
].y
++;
298 void stats_hist_print(stats_container_t
*hist
)
301 for (i
= 0; i
< hist
->size
; i
++) {
302 x
= hist
->records
[i
].x
;
303 if (i
< hist
->size
-1)
304 printf("[%ld,%ld) = %ld\n", x
,
305 hist
->records
[i
+1].x
, hist
->records
[i
].y
);
307 printf("[%ld,-) = %ld\n", x
, hist
->records
[i
].y
);
311 int stats_container_save(char *filename
, char *title
, char *xlabel
, char *ylabel
, stats_container_t
*data
, char *mode
)
314 int minx
= 0, maxx
= 0, miny
= 0, maxy
= 0;
324 /* generate the filenames */
325 if (asprintf(&datfile
, "%s.dat", filename
) == -1) {
326 fprintf(stderr
, "Failed to allocate string for data filename\n");
329 if (asprintf(&pltfile
, "%s.plt", filename
) == -1) {
330 fprintf(stderr
, "Failed to allocate string for plot filename\n");
334 /* generate the data file */
335 if (!(dat_fd
= fopen(datfile
, "w"))) {
336 perror("Failed to open dat file");
339 minx
= maxx
= data
->records
[0].x
;
340 miny
= maxy
= data
->records
[0].y
;
341 for (i
= 0; i
<= data
->index
; i
++) {
342 rec
= &data
->records
[i
];
343 minx
= MIN(minx
, rec
->x
);
344 maxx
= MAX(maxx
, rec
->x
);
345 miny
= MIN(miny
, rec
->y
);
346 maxy
= MAX(maxy
, rec
->y
);
347 fprintf(dat_fd
, "%ld %ld\n", rec
->x
, rec
->y
);
352 /* generate the plt file */
353 if (!(plt_fd
= fopen(pltfile
, "w"))) {
354 perror("Failed to open plt file");
357 fprintf(plt_fd
, "set terminal png\n");
358 fprintf(plt_fd
, "set output \"%s.png\"\n", pltfile
);
359 fprintf(plt_fd
, "set title \"%s\"\n", title
);
360 fprintf(plt_fd
, "set xlabel \"%s\"\n", xlabel
);
361 fprintf(plt_fd
, "set ylabel \"%s\"\n", ylabel
);
364 //fprintf(plt_fd, "plot [%d:%d] [%d:%d] \"%s\" with %s\n",
365 // minx, maxx, miny, maxy, datfile, mode);
367 // expanded range mode - slightly more intuitive I think...
368 fprintf(plt_fd
, "plot [0:%d] [0:%d] \"%s\" with %s\n",
369 maxx
+1, maxy
+1, datfile
, mode
);