1
/*
2
 * Copyright © 2006 Red Hat, Inc.
3
 *
4
 * Permission to use, copy, modify, distribute, and sell this software
5
 * and its documentation for any purpose is hereby granted without
6
 * fee, provided that the above copyright notice appear in all copies
7
 * and that both that copyright notice and this permission notice
8
 * appear in supporting documentation, and that the name of
9
 * the authors not be used in advertising or publicity pertaining to
10
 * distribution of the software without specific, written prior
11
 * permission. The authors make no representations about the
12
 * suitability of this software for any purpose.  It is provided "as
13
 * is" without express or implied warranty.
14
 *
15
 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
16
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17
 * FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL,
18
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
21
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
 *
23
 * Authors: Carl Worth <cworth@cworth.org>
24
 */
25

            
26
#include "cairo-stats.h"
27

            
28
#include <assert.h>
29

            
30
void
31
_cairo_stats_compute (cairo_stats_t *stats,
32
		      cairo_time_t  *values,
33
		      int	     num_values)
34
{
35
    cairo_time_t sum, mean, q1, q3, iqr;
36
    cairo_time_t outlier_min, outlier_max;
37
    int i, min_valid, num_valid;
38
    double s;
39

            
40
    assert (num_values > 0);
41

            
42
    if (num_values == 1) {
43
	stats->min_ticks = stats->median_ticks = values[0];
44
	stats->std_dev = 0;
45
	stats->iterations = 1;
46
	stats->values = values;
47
	return;
48
    }
49

            
50
    /* First, identify any outliers, using the definition of "mild
51
     * outliers" from:
52
     *
53
     *		http://en.wikipedia.org/wiki/Outliers
54
     *
55
     * Which is that outliers are any values less than Q1 - 1.5 * IQR
56
     * or greater than Q3 + 1.5 * IQR where Q1 and Q3 are the first
57
     * and third quartiles and IQR is the inter-quartile range (Q3 -
58
     * Q1).
59
     */
60
    num_valid = num_values;
61
    do {
62
	num_values = num_valid;
63
	qsort (values, num_values, sizeof (cairo_time_t), _cairo_time_cmp);
64

            
65
	q1 = values[1*num_values/4];
66
	q3 = values[3*num_values/4];
67

            
68
	/* XXX assumes we have native uint64_t */
69
	iqr = q3 - q1;
70
	outlier_min = q1 - 3 * iqr / 2;
71
	outlier_max = q3 + 3 * iqr / 2;
72

            
73
	for (i = 0; i < num_values && values[i] < outlier_min; i++)
74
	    ;
75
	min_valid = i;
76

            
77
	for (i = 0; i < num_values && values[i] <= outlier_max; i++)
78
	    ;
79
	num_valid = i - min_valid;
80
	assert(num_valid);
81
	values += min_valid;
82
    } while (num_valid != num_values);
83

            
84
    stats->values = values;
85
    stats->iterations = num_valid;
86
    stats->min_ticks = values[0];
87
    stats->median_ticks = values[num_valid / 2];
88

            
89
    sum = 0;
90
    for (i = 0; i < num_valid; i++)
91
	sum = _cairo_time_add (sum, values[i]);
92
    mean = sum / num_valid;
93

            
94
    /* Let's use a normalized std. deviation for easier comparison. */
95
    s = 0;
96
    for (i = 0; i < num_valid; i++) {
97
	double delta = (values[i] - mean) / (double)mean;
98
	s += delta * delta;
99
    }
100
    stats->std_dev = sqrt(s / num_valid);
101
}
102

            
103
cairo_bool_t
104
_cairo_histogram_init (cairo_histogram_t *h,
105
		       int width, int height)
106
{
107
    h->width = width;
108
    h->height = height;
109
    if (h->width < 2 || h->height < 1)
110
	return FALSE;
111

            
112
    h->num_columns = width - 2;
113
    h->num_rows = height - 1;
114
    h->columns = malloc (sizeof(int)*h->num_columns);
115
    return h->columns != NULL;
116
}
117

            
118
cairo_bool_t
119
_cairo_histogram_compute (cairo_histogram_t *h,
120
			  const cairo_time_t *values,
121
			  int num_values)
122
{
123
    cairo_time_t delta;
124
    int i;
125

            
126
    if (num_values == 0)
127
	return FALSE;
128

            
129
    h->min_value = values[0];
130
    h->max_value = values[0];
131

            
132
    for (i = 1; i < num_values; i++) {
133
	if (values[i] < h->min_value)
134
	    h->min_value = values[i];
135
	if (values[i] > h->max_value)
136
	    h->max_value = values[i];
137
    }
138

            
139
    delta = h->max_value - h->min_value;
140
    if (delta == 0)
141
	return FALSE;
142

            
143
    memset(h->columns, 0, sizeof(int)*h->num_columns);
144
    h->max_count = 0;
145

            
146
    for (i = 0; i < num_values; i++) {
147
	int count = h->columns[(values[i] - h->min_value) * (h->num_columns - 1) / delta]++;
148
	if (count > h->max_count)
149
	    h->max_count = count;
150
    }
151

            
152
    return TRUE;
153
}
154

            
155
void
156
_cairo_histogram_printf (cairo_histogram_t *h,
157
			 FILE *file)
158
{
159
    int x, y, num_rows;
160

            
161
    num_rows = h->num_rows;
162
    if (h->max_count < num_rows)
163
	num_rows = h->max_count;
164
    for (y = 0; y < num_rows; y++) {
165
	int min_count = ((num_rows - y - 1) * h->max_count) / num_rows + h->max_count / (2*num_rows);
166
	fprintf (file, "|");
167
	for (x = 0; x < h->num_columns; x++)
168
	    fprintf (file, "%c", h->columns[x] > min_count ? 'x' : ' ');
169
	fprintf (file, "|\n");
170
    }
171

            
172
    fprintf(file, ".");
173
    for (x = 0; x < h->num_columns; x++)
174
	fprintf (file, "-");
175
    fprintf (file, ".\n");
176
}
177

            
178
void
179
_cairo_histogram_fini (cairo_histogram_t *h)
180
{
181
    free(h->columns);
182
}