1118856Sphk/*
2118856Sphk * ----------------------------------------------------------------------------
3118856Sphk * "THE BEER-WARE LICENSE" (Revision 42):
4118856Sphk * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5118856Sphk * can do whatever you want with this stuff. If we meet some day, and you think
6118856Sphk * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7118856Sphk * ----------------------------------------------------------------------------
8118856Sphk *
9118856Sphk */
10118856Sphk
11118856Sphk#include <sys/cdefs.h>
12118856Sphk__FBSDID("$FreeBSD$");
13118856Sphk
14118856Sphk#include <stdio.h>
15118856Sphk#include <math.h>
16118856Sphk#include <err.h>
17118856Sphk#include <string.h>
18118856Sphk#include <stdlib.h>
19118856Sphk#include <unistd.h>
20155894Smdodd#include <sys/ioctl.h>
21118856Sphk#include <sys/queue.h>
22155894Smdodd#include <sys/ttycom.h>
23118856Sphk
24118856Sphk#define NSTUDENT 100
25118856Sphk#define NCONF 6
26227173Sedstatic double const studentpct[] = { 80, 90, 95, 98, 99, 99.5 };
27227173Sedstatic double student[NSTUDENT + 1][NCONF] = {
28118856Sphk/* inf */	{	1.282,	1.645,	1.960,	2.326,	2.576,	3.090  },
29118856Sphk/* 1. */	{	3.078,	6.314,	12.706,	31.821,	63.657,	318.313  },
30118856Sphk/* 2. */	{	1.886,	2.920,	4.303,	6.965,	9.925,	22.327  },
31118856Sphk/* 3. */	{	1.638,	2.353,	3.182,	4.541,	5.841,	10.215  },
32118856Sphk/* 4. */	{	1.533,	2.132,	2.776,	3.747,	4.604,	7.173  },
33118856Sphk/* 5. */	{	1.476,	2.015,	2.571,	3.365,	4.032,	5.893  },
34118856Sphk/* 6. */	{	1.440,	1.943,	2.447,	3.143,	3.707,	5.208  },
35118856Sphk/* 7. */	{	1.415,	1.895,	2.365,	2.998,	3.499,	4.782  },
36118856Sphk/* 8. */	{	1.397,	1.860,	2.306,	2.896,	3.355,	4.499  },
37118856Sphk/* 9. */	{	1.383,	1.833,	2.262,	2.821,	3.250,	4.296  },
38118856Sphk/* 10. */	{	1.372,	1.812,	2.228,	2.764,	3.169,	4.143  },
39118856Sphk/* 11. */	{	1.363,	1.796,	2.201,	2.718,	3.106,	4.024  },
40118856Sphk/* 12. */	{	1.356,	1.782,	2.179,	2.681,	3.055,	3.929  },
41118856Sphk/* 13. */	{	1.350,	1.771,	2.160,	2.650,	3.012,	3.852  },
42118856Sphk/* 14. */	{	1.345,	1.761,	2.145,	2.624,	2.977,	3.787  },
43118856Sphk/* 15. */	{	1.341,	1.753,	2.131,	2.602,	2.947,	3.733  },
44118856Sphk/* 16. */	{	1.337,	1.746,	2.120,	2.583,	2.921,	3.686  },
45118856Sphk/* 17. */	{	1.333,	1.740,	2.110,	2.567,	2.898,	3.646  },
46118856Sphk/* 18. */	{	1.330,	1.734,	2.101,	2.552,	2.878,	3.610  },
47118856Sphk/* 19. */	{	1.328,	1.729,	2.093,	2.539,	2.861,	3.579  },
48118856Sphk/* 20. */	{	1.325,	1.725,	2.086,	2.528,	2.845,	3.552  },
49118856Sphk/* 21. */	{	1.323,	1.721,	2.080,	2.518,	2.831,	3.527  },
50118856Sphk/* 22. */	{	1.321,	1.717,	2.074,	2.508,	2.819,	3.505  },
51118856Sphk/* 23. */	{	1.319,	1.714,	2.069,	2.500,	2.807,	3.485  },
52118856Sphk/* 24. */	{	1.318,	1.711,	2.064,	2.492,	2.797,	3.467  },
53118856Sphk/* 25. */	{	1.316,	1.708,	2.060,	2.485,	2.787,	3.450  },
54118856Sphk/* 26. */	{	1.315,	1.706,	2.056,	2.479,	2.779,	3.435  },
55118856Sphk/* 27. */	{	1.314,	1.703,	2.052,	2.473,	2.771,	3.421  },
56118856Sphk/* 28. */	{	1.313,	1.701,	2.048,	2.467,	2.763,	3.408  },
57118856Sphk/* 29. */	{	1.311,	1.699,	2.045,	2.462,	2.756,	3.396  },
58118856Sphk/* 30. */	{	1.310,	1.697,	2.042,	2.457,	2.750,	3.385  },
59118856Sphk/* 31. */	{	1.309,	1.696,	2.040,	2.453,	2.744,	3.375  },
60118856Sphk/* 32. */	{	1.309,	1.694,	2.037,	2.449,	2.738,	3.365  },
61118856Sphk/* 33. */	{	1.308,	1.692,	2.035,	2.445,	2.733,	3.356  },
62118856Sphk/* 34. */	{	1.307,	1.691,	2.032,	2.441,	2.728,	3.348  },
63118856Sphk/* 35. */	{	1.306,	1.690,	2.030,	2.438,	2.724,	3.340  },
64118856Sphk/* 36. */	{	1.306,	1.688,	2.028,	2.434,	2.719,	3.333  },
65118856Sphk/* 37. */	{	1.305,	1.687,	2.026,	2.431,	2.715,	3.326  },
66118856Sphk/* 38. */	{	1.304,	1.686,	2.024,	2.429,	2.712,	3.319  },
67118856Sphk/* 39. */	{	1.304,	1.685,	2.023,	2.426,	2.708,	3.313  },
68118856Sphk/* 40. */	{	1.303,	1.684,	2.021,	2.423,	2.704,	3.307  },
69118856Sphk/* 41. */	{	1.303,	1.683,	2.020,	2.421,	2.701,	3.301  },
70118856Sphk/* 42. */	{	1.302,	1.682,	2.018,	2.418,	2.698,	3.296  },
71118856Sphk/* 43. */	{	1.302,	1.681,	2.017,	2.416,	2.695,	3.291  },
72118856Sphk/* 44. */	{	1.301,	1.680,	2.015,	2.414,	2.692,	3.286  },
73118856Sphk/* 45. */	{	1.301,	1.679,	2.014,	2.412,	2.690,	3.281  },
74118856Sphk/* 46. */	{	1.300,	1.679,	2.013,	2.410,	2.687,	3.277  },
75118856Sphk/* 47. */	{	1.300,	1.678,	2.012,	2.408,	2.685,	3.273  },
76118856Sphk/* 48. */	{	1.299,	1.677,	2.011,	2.407,	2.682,	3.269  },
77118856Sphk/* 49. */	{	1.299,	1.677,	2.010,	2.405,	2.680,	3.265  },
78118856Sphk/* 50. */	{	1.299,	1.676,	2.009,	2.403,	2.678,	3.261  },
79118856Sphk/* 51. */	{	1.298,	1.675,	2.008,	2.402,	2.676,	3.258  },
80118856Sphk/* 52. */	{	1.298,	1.675,	2.007,	2.400,	2.674,	3.255  },
81118856Sphk/* 53. */	{	1.298,	1.674,	2.006,	2.399,	2.672,	3.251  },
82118856Sphk/* 54. */	{	1.297,	1.674,	2.005,	2.397,	2.670,	3.248  },
83118856Sphk/* 55. */	{	1.297,	1.673,	2.004,	2.396,	2.668,	3.245  },
84118856Sphk/* 56. */	{	1.297,	1.673,	2.003,	2.395,	2.667,	3.242  },
85118856Sphk/* 57. */	{	1.297,	1.672,	2.002,	2.394,	2.665,	3.239  },
86118856Sphk/* 58. */	{	1.296,	1.672,	2.002,	2.392,	2.663,	3.237  },
87118856Sphk/* 59. */	{	1.296,	1.671,	2.001,	2.391,	2.662,	3.234  },
88118856Sphk/* 60. */	{	1.296,	1.671,	2.000,	2.390,	2.660,	3.232  },
89118856Sphk/* 61. */	{	1.296,	1.670,	2.000,	2.389,	2.659,	3.229  },
90118856Sphk/* 62. */	{	1.295,	1.670,	1.999,	2.388,	2.657,	3.227  },
91118856Sphk/* 63. */	{	1.295,	1.669,	1.998,	2.387,	2.656,	3.225  },
92118856Sphk/* 64. */	{	1.295,	1.669,	1.998,	2.386,	2.655,	3.223  },
93118856Sphk/* 65. */	{	1.295,	1.669,	1.997,	2.385,	2.654,	3.220  },
94118856Sphk/* 66. */	{	1.295,	1.668,	1.997,	2.384,	2.652,	3.218  },
95118856Sphk/* 67. */	{	1.294,	1.668,	1.996,	2.383,	2.651,	3.216  },
96118856Sphk/* 68. */	{	1.294,	1.668,	1.995,	2.382,	2.650,	3.214  },
97118856Sphk/* 69. */	{	1.294,	1.667,	1.995,	2.382,	2.649,	3.213  },
98118856Sphk/* 70. */	{	1.294,	1.667,	1.994,	2.381,	2.648,	3.211  },
99118856Sphk/* 71. */	{	1.294,	1.667,	1.994,	2.380,	2.647,	3.209  },
100118856Sphk/* 72. */	{	1.293,	1.666,	1.993,	2.379,	2.646,	3.207  },
101118856Sphk/* 73. */	{	1.293,	1.666,	1.993,	2.379,	2.645,	3.206  },
102118856Sphk/* 74. */	{	1.293,	1.666,	1.993,	2.378,	2.644,	3.204  },
103118856Sphk/* 75. */	{	1.293,	1.665,	1.992,	2.377,	2.643,	3.202  },
104118856Sphk/* 76. */	{	1.293,	1.665,	1.992,	2.376,	2.642,	3.201  },
105118856Sphk/* 77. */	{	1.293,	1.665,	1.991,	2.376,	2.641,	3.199  },
106118856Sphk/* 78. */	{	1.292,	1.665,	1.991,	2.375,	2.640,	3.198  },
107118856Sphk/* 79. */	{	1.292,	1.664,	1.990,	2.374,	2.640,	3.197  },
108118856Sphk/* 80. */	{	1.292,	1.664,	1.990,	2.374,	2.639,	3.195  },
109118856Sphk/* 81. */	{	1.292,	1.664,	1.990,	2.373,	2.638,	3.194  },
110118856Sphk/* 82. */	{	1.292,	1.664,	1.989,	2.373,	2.637,	3.193  },
111118856Sphk/* 83. */	{	1.292,	1.663,	1.989,	2.372,	2.636,	3.191  },
112118856Sphk/* 84. */	{	1.292,	1.663,	1.989,	2.372,	2.636,	3.190  },
113118856Sphk/* 85. */	{	1.292,	1.663,	1.988,	2.371,	2.635,	3.189  },
114118856Sphk/* 86. */	{	1.291,	1.663,	1.988,	2.370,	2.634,	3.188  },
115118856Sphk/* 87. */	{	1.291,	1.663,	1.988,	2.370,	2.634,	3.187  },
116118856Sphk/* 88. */	{	1.291,	1.662,	1.987,	2.369,	2.633,	3.185  },
117118856Sphk/* 89. */	{	1.291,	1.662,	1.987,	2.369,	2.632,	3.184  },
118118856Sphk/* 90. */	{	1.291,	1.662,	1.987,	2.368,	2.632,	3.183  },
119118856Sphk/* 91. */	{	1.291,	1.662,	1.986,	2.368,	2.631,	3.182  },
120118856Sphk/* 92. */	{	1.291,	1.662,	1.986,	2.368,	2.630,	3.181  },
121118856Sphk/* 93. */	{	1.291,	1.661,	1.986,	2.367,	2.630,	3.180  },
122118856Sphk/* 94. */	{	1.291,	1.661,	1.986,	2.367,	2.629,	3.179  },
123118856Sphk/* 95. */	{	1.291,	1.661,	1.985,	2.366,	2.629,	3.178  },
124118856Sphk/* 96. */	{	1.290,	1.661,	1.985,	2.366,	2.628,	3.177  },
125118856Sphk/* 97. */	{	1.290,	1.661,	1.985,	2.365,	2.627,	3.176  },
126118856Sphk/* 98. */	{	1.290,	1.661,	1.984,	2.365,	2.627,	3.175  },
127118856Sphk/* 99. */	{	1.290,	1.660,	1.984,	2.365,	2.626,	3.175  },
128118856Sphk/* 100. */	{	1.290,	1.660,	1.984,	2.364,	2.626,	3.174  }
129118856Sphk};
130118856Sphk
131144993Smdodd#define	MAX_DS	8
132144993Smdoddstatic char symbol[MAX_DS] = { ' ', 'x', '+', '*', '%', '#', '@', 'O' };
133144993Smdodd
134118856Sphkstruct dataset {
135158246Sphk	char *name;
136183960Sphk	double	*points;
137183960Sphk	unsigned lpoints;
138118856Sphk	double sy, syy;
139189934Sdwmalone	unsigned n;
140118856Sphk};
141118856Sphk
142118856Sphkstatic struct dataset *
143118856SphkNewSet(void)
144118856Sphk{
145118856Sphk	struct dataset *ds;
146118856Sphk
147118856Sphk	ds = calloc(1, sizeof *ds);
148183960Sphk	ds->lpoints = 100000;
149183960Sphk	ds->points = calloc(sizeof *ds->points, ds->lpoints);
150118856Sphk	return(ds);
151118856Sphk}
152118856Sphk
153118856Sphkstatic void
154118856SphkAddPoint(struct dataset *ds, double a)
155118856Sphk{
156183960Sphk	double *dp;
157118856Sphk
158183960Sphk	if (ds->n >= ds->lpoints) {
159183960Sphk		dp = ds->points;
160183960Sphk		ds->lpoints *= 4;
161183960Sphk		ds->points = calloc(sizeof *ds->points, ds->lpoints);
162183960Sphk		memcpy(ds->points, dp, sizeof *dp * ds->n);
163183961Sphk		free(dp);
164183960Sphk	}
165183960Sphk	ds->points[ds->n++] = a;
166118856Sphk	ds->sy += a;
167118856Sphk	ds->syy += a * a;
168118856Sphk}
169118856Sphk
170118856Sphkstatic double
171118856SphkMin(struct dataset *ds)
172118856Sphk{
173118856Sphk
174183960Sphk	return (ds->points[0]);
175118856Sphk}
176118856Sphk
177118856Sphkstatic double
178118856SphkMax(struct dataset *ds)
179118856Sphk{
180118856Sphk
181183960Sphk	return (ds->points[ds->n -1]);
182118856Sphk}
183118856Sphk
184118856Sphkstatic double
185118856SphkAvg(struct dataset *ds)
186118856Sphk{
187118856Sphk
188118856Sphk	return(ds->sy / ds->n);
189118856Sphk}
190118856Sphk
191118856Sphkstatic double
192118856SphkMedian(struct dataset *ds)
193118856Sphk{
194118856Sphk
195183960Sphk	return (ds->points[ds->n / 2]);
196118856Sphk}
197118856Sphk
198118856Sphkstatic double
199118856SphkVar(struct dataset *ds)
200118856Sphk{
201118856Sphk
202118856Sphk	return (ds->syy - ds->sy * ds->sy / ds->n) / (ds->n - 1.0);
203118856Sphk}
204118856Sphk
205118856Sphkstatic double
206118856SphkStddev(struct dataset *ds)
207118856Sphk{
208118856Sphk
209118856Sphk	return sqrt(Var(ds));
210118856Sphk}
211118856Sphk
212118856Sphkstatic void
213118856SphkVitalsHead(void)
214118856Sphk{
215118856Sphk
216118856Sphk	printf("    N           Min           Max        Median           Avg        Stddev\n");
217118856Sphk}
218118856Sphk
219118856Sphkstatic void
220118856SphkVitals(struct dataset *ds, int flag)
221118856Sphk{
222118856Sphk
223144993Smdodd	printf("%c %3d %13.8g %13.8g %13.8g %13.8g %13.8g", symbol[flag],
224118856Sphk	    ds->n, Min(ds), Max(ds), Median(ds), Avg(ds), Stddev(ds));
225118856Sphk	printf("\n");
226118856Sphk}
227118856Sphk
228118856Sphkstatic void
229118856SphkRelative(struct dataset *ds, struct dataset *rs, int confidx)
230118856Sphk{
231118856Sphk	double spool, s, d, e, t;
232176106Sdwmalone	int i;
233118856Sphk
234118856Sphk	i = ds->n + rs->n - 2;
235118856Sphk	if (i > NSTUDENT)
236118856Sphk		t = student[0][confidx];
237118856Sphk	else
238118856Sphk		t = student[i][confidx];
239118856Sphk	spool = (ds->n - 1) * Var(ds) + (rs->n - 1) * Var(rs);
240118856Sphk	spool /= ds->n + rs->n - 2;
241118856Sphk	spool = sqrt(spool);
242118856Sphk	s = spool * sqrt(1.0 / ds->n + 1.0 / rs->n);
243118856Sphk	d = Avg(ds) - Avg(rs);
244118856Sphk	e = t * s;
245118856Sphk
246118856Sphk	if (fabs(d) > e) {
247118856Sphk
248118856Sphk		printf("Difference at %.1f%% confidence\n", studentpct[confidx]);
249118856Sphk		printf("	%g +/- %g\n", d, e);
250118856Sphk		printf("	%g%% +/- %g%%\n", d * 100 / Avg(rs), e * 100 / Avg(rs));
251118856Sphk		printf("	(Student's t, pooled s = %g)\n", spool);
252118856Sphk	} else {
253118856Sphk		printf("No difference proven at %.1f%% confidence\n",
254118856Sphk		    studentpct[confidx]);
255118856Sphk	}
256118856Sphk}
257118856Sphk
258118856Sphkstruct plot {
259118856Sphk	double		min;
260118856Sphk	double		max;
261118856Sphk	double		span;
262118856Sphk	int		width;
263118856Sphk
264118856Sphk	double		x0, dx;
265118856Sphk	int		height;
266118856Sphk	char		*data;
267121795Sphk	char		**bar;
268121795Sphk	int		separate_bars;
269148224Sphk	int		num_datasets;
270118856Sphk};
271118856Sphk
272118856Sphkstatic struct plot plot;
273118856Sphk
274118856Sphkstatic void
275148224SphkSetupPlot(int width, int separate, int num_datasets)
276118856Sphk{
277118856Sphk	struct plot *pl;
278118856Sphk
279118856Sphk	pl = &plot;
280118856Sphk	pl->width = width;
281118856Sphk	pl->height = 0;
282118856Sphk	pl->data = NULL;
283121795Sphk	pl->bar = NULL;
284121795Sphk	pl->separate_bars = separate;
285148224Sphk	pl->num_datasets = num_datasets;
286118856Sphk	pl->min = 999e99;
287118856Sphk	pl->max = -999e99;
288118856Sphk}
289118856Sphk
290118856Sphkstatic void
291118856SphkAdjPlot(double a)
292118856Sphk{
293118856Sphk	struct plot *pl;
294118856Sphk
295118856Sphk	pl = &plot;
296118856Sphk	if (a < pl->min)
297118856Sphk		pl->min = a;
298118856Sphk	if (a > pl->max)
299118856Sphk		pl->max = a;
300118856Sphk	pl->span = pl->max - pl->min;
301118856Sphk	pl->dx = pl->span / (pl->width - 1.0);
302118856Sphk	pl->x0 = pl->min - .5 * pl->dx;
303118856Sphk}
304118856Sphk
305118856Sphkstatic void
306118856SphkDimPlot(struct dataset *ds)
307118856Sphk{
308118856Sphk	AdjPlot(Min(ds));
309118856Sphk	AdjPlot(Max(ds));
310118856Sphk	AdjPlot(Avg(ds) - Stddev(ds));
311118856Sphk	AdjPlot(Avg(ds) + Stddev(ds));
312118856Sphk}
313118856Sphk
314118856Sphkstatic void
315118856SphkPlotSet(struct dataset *ds, int val)
316118856Sphk{
317118856Sphk	struct plot *pl;
318189934Sdwmalone	int i, j, m, x;
319189934Sdwmalone	unsigned n;
320121795Sphk	int bar;
321118856Sphk
322118856Sphk	pl = &plot;
323119067Sphk	if (pl->span == 0)
324119067Sphk		return;
325121795Sphk
326121795Sphk	if (pl->separate_bars)
327121795Sphk		bar = val-1;
328121795Sphk	else
329121795Sphk		bar = 0;
330121795Sphk
331121795Sphk	if (pl->bar == NULL) {
332148224Sphk		pl->bar = malloc(sizeof(char *) * pl->num_datasets);
333148224Sphk		memset(pl->bar, 0, sizeof(char*) * pl->num_datasets);
334121795Sphk	}
335121795Sphk	if (pl->bar[bar] == NULL) {
336121795Sphk		pl->bar[bar] = malloc(pl->width);
337121795Sphk		memset(pl->bar[bar], 0, pl->width);
338121795Sphk	}
339121795Sphk
340118856Sphk	m = 1;
341118856Sphk	i = -1;
342118856Sphk	j = 0;
343183960Sphk	for (n = 0; n < ds->n; n++) {
344183960Sphk		x = (ds->points[n] - pl->x0) / pl->dx;
345118856Sphk		if (x == i) {
346118856Sphk			j++;
347118856Sphk			if (j > m)
348118856Sphk				m = j;
349118856Sphk		} else {
350118856Sphk			j = 1;
351118856Sphk			i = x;
352118856Sphk		}
353118856Sphk	}
354118856Sphk	m += 1;
355118856Sphk	if (m > pl->height) {
356118856Sphk		pl->data = realloc(pl->data, pl->width * m);
357118856Sphk		memset(pl->data + pl->height * pl->width, 0,
358118856Sphk		    (m - pl->height) * pl->width);
359118856Sphk	}
360118856Sphk	pl->height = m;
361118856Sphk	i = -1;
362183960Sphk	for (n = 0; n < ds->n; n++) {
363183960Sphk		x = (ds->points[n] - pl->x0) / pl->dx;
364118856Sphk		if (x == i) {
365118856Sphk			j++;
366118856Sphk		} else {
367118856Sphk			j = 1;
368118856Sphk			i = x;
369118856Sphk		}
370118856Sphk		pl->data[j * pl->width + x] |= val;
371118856Sphk	}
372158246Sphk	if (!isnan(Stddev(ds))) {
373158246Sphk		x = ((Avg(ds) - Stddev(ds)) - pl->x0) / pl->dx;
374158246Sphk		m = ((Avg(ds) + Stddev(ds)) - pl->x0) / pl->dx;
375158246Sphk		pl->bar[bar][m] = '|';
376158246Sphk		pl->bar[bar][x] = '|';
377158246Sphk		for (i = x + 1; i < m; i++)
378158246Sphk			if (pl->bar[bar][i] == 0)
379158246Sphk				pl->bar[bar][i] = '_';
380158246Sphk	}
381118856Sphk	x = (Median(ds) - pl->x0) / pl->dx;
382121795Sphk	pl->bar[bar][x] = 'M';
383118856Sphk	x = (Avg(ds) - pl->x0) / pl->dx;
384121795Sphk	pl->bar[bar][x] = 'A';
385118856Sphk}
386118856Sphk
387118856Sphkstatic void
388118856SphkDumpPlot(void)
389118856Sphk{
390118856Sphk	struct plot *pl;
391118856Sphk	int i, j, k;
392118856Sphk
393118856Sphk	pl = &plot;
394119067Sphk	if (pl->span == 0) {
395119067Sphk		printf("[no plot, span is zero width]\n");
396119067Sphk		return;
397119067Sphk	}
398118856Sphk
399118856Sphk	putchar('+');
400118856Sphk	for (i = 0; i < pl->width; i++)
401118856Sphk		putchar('-');
402118856Sphk	putchar('+');
403118856Sphk	putchar('\n');
404118856Sphk	for (i = 1; i < pl->height; i++) {
405118856Sphk		putchar('|');
406118856Sphk		for (j = 0; j < pl->width; j++) {
407118856Sphk			k = pl->data[(pl->height - i) * pl->width + j];
408144993Smdodd			if (k >= 0 && k < MAX_DS)
409144993Smdodd				putchar(symbol[k]);
410144993Smdodd			else
411144993Smdodd				printf("[%02x]", k);
412118856Sphk		}
413118856Sphk		putchar('|');
414118856Sphk		putchar('\n');
415118856Sphk	}
416148224Sphk	for (i = 0; i < pl->num_datasets; i++) {
417121795Sphk		if (pl->bar[i] == NULL)
418121795Sphk			continue;
419121795Sphk		putchar('|');
420121795Sphk		for (j = 0; j < pl->width; j++) {
421121795Sphk			k = pl->bar[i][j];
422121795Sphk			if (k == 0)
423121795Sphk				k = ' ';
424121795Sphk			putchar(k);
425121795Sphk		}
426121795Sphk		putchar('|');
427121795Sphk		putchar('\n');
428118856Sphk	}
429118856Sphk	putchar('+');
430118856Sphk	for (i = 0; i < pl->width; i++)
431118856Sphk		putchar('-');
432118856Sphk	putchar('+');
433118856Sphk	putchar('\n');
434118856Sphk}
435118856Sphk
436183960Sphkstatic int
437183960Sphkdbl_cmp(const void *a, const void *b)
438183960Sphk{
439183960Sphk	const double *aa = a;
440183960Sphk	const double *bb = b;
441118856Sphk
442183960Sphk	if (*aa < *bb)
443183960Sphk		return (-1);
444183960Sphk	else if (*aa > *bb)
445183960Sphk		return (1);
446183960Sphk	else
447183960Sphk		return (0);
448183960Sphk}
449183960Sphk
450118856Sphkstatic struct dataset *
451176106SdwmaloneReadSet(const char *n, int column, const char *delim)
452118856Sphk{
453118856Sphk	FILE *f;
454161692Sphk	char buf[BUFSIZ], *p, *t;
455118856Sphk	struct dataset *s;
456118856Sphk	double d;
457118856Sphk	int line;
458161692Sphk	int i;
459118856Sphk
460118856Sphk	if (n == NULL) {
461118856Sphk		f = stdin;
462118856Sphk		n = "<stdin>";
463118856Sphk	} else if (!strcmp(n, "-")) {
464118856Sphk		f = stdin;
465118856Sphk		n = "<stdin>";
466118856Sphk	} else {
467118856Sphk		f = fopen(n, "r");
468118856Sphk	}
469118856Sphk	if (f == NULL)
470118856Sphk		err(1, "Cannot open %s", n);
471118856Sphk	s = NewSet();
472158246Sphk	s->name = strdup(n);
473118856Sphk	line = 0;
474118856Sphk	while (fgets(buf, sizeof buf, f) != NULL) {
475118856Sphk		line++;
476161692Sphk
477161692Sphk		i = strlen(buf);
478161692Sphk		if (buf[i-1] == '\n')
479161692Sphk			buf[i-1] = '\0';
480161692Sphk		for (i = 1, t = strtok(buf, delim);
481161692Sphk		     t != NULL && *t != '#';
482161692Sphk		     i++, t = strtok(NULL, delim)) {
483161692Sphk			if (i == column)
484161692Sphk				break;
485118856Sphk		}
486161692Sphk		if (t == NULL || *t == '#')
487161692Sphk			continue;
488161692Sphk
489161692Sphk		d = strtod(t, &p);
490118856Sphk		if (p != NULL && *p != '\0')
491118856Sphk			err(2, "Invalid data on line %d in %s\n", line, n);
492118856Sphk		if (*buf != '\0')
493118856Sphk			AddPoint(s, d);
494118856Sphk	}
495118856Sphk	fclose(f);
496118856Sphk	if (s->n < 3) {
497118856Sphk		fprintf(stderr,
498118856Sphk		    "Dataset %s must contain at least 3 data points\n", n);
499118856Sphk		exit (2);
500118856Sphk	}
501183960Sphk	qsort(s->points, s->n, sizeof *s->points, dbl_cmp);
502118856Sphk	return (s);
503118856Sphk}
504118856Sphk
505118856Sphkstatic void
506118856Sphkusage(char const *whine)
507118856Sphk{
508118856Sphk	int i;
509118856Sphk
510118856Sphk	fprintf(stderr, "%s\n", whine);
511118856Sphk	fprintf(stderr,
512243079Seadler	    "Usage: ministat [-C column] [-c confidence] [-d delimiter(s)] [-Ans] [-w width] [file [file ...]]\n");
513118856Sphk	fprintf(stderr, "\tconfidence = {");
514118856Sphk	for (i = 0; i < NCONF; i++) {
515118856Sphk		fprintf(stderr, "%s%g%%",
516118856Sphk		    i ? ", " : "",
517118856Sphk		    studentpct[i]);
518118856Sphk	}
519118856Sphk	fprintf(stderr, "}\n");
520243079Seadler	fprintf(stderr, "\t-A : print statistics only. suppress the graph.\n");
521161692Sphk	fprintf(stderr, "\t-C : column number to extract (starts and defaults to 1)\n");
522161692Sphk	fprintf(stderr, "\t-d : delimiter(s) string, default to \" \\t\"\n");
523146689Srwatson	fprintf(stderr, "\t-n : print summary statistics only, no graph/test\n");
524121795Sphk	fprintf(stderr, "\t-s : print avg/median/stddev bars on separate lines\n");
525155894Smdodd	fprintf(stderr, "\t-w : width of graph/test output (default 74 or terminal width)\n");
526118856Sphk	exit (2);
527118856Sphk}
528118856Sphk
529118856Sphkint
530118856Sphkmain(int argc, char **argv)
531118856Sphk{
532144993Smdodd	struct dataset *ds[7];
533144993Smdodd	int nds;
534118856Sphk	double a;
535176106Sdwmalone	const char *delim = " \t";
536118856Sphk	char *p;
537118856Sphk	int c, i, ci;
538161692Sphk	int column = 1;
539121795Sphk	int flag_s = 0;
540146689Srwatson	int flag_n = 0;
541155894Smdodd	int termwidth = 74;
542243079Seadler	int suppress_plot = 0;
543118856Sphk
544155894Smdodd	if (isatty(STDOUT_FILENO)) {
545155894Smdodd		struct winsize wsz;
546155894Smdodd
547155894Smdodd		if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
548155894Smdodd			termwidth = atoi(p);
549155894Smdodd		else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wsz) != -1 &&
550155894Smdodd			 wsz.ws_col > 0)
551155894Smdodd			termwidth = wsz.ws_col - 2;
552155894Smdodd	}
553155894Smdodd
554118856Sphk	ci = -1;
555243079Seadler	while ((c = getopt(argc, argv, "AC:c:d:snw:")) != -1)
556118856Sphk		switch (c) {
557243079Seadler		case 'A':
558243079Seadler			suppress_plot = 1;
559243079Seadler			break;
560161692Sphk		case 'C':
561161692Sphk			column = strtol(optarg, &p, 10);
562161692Sphk			if (p != NULL && *p != '\0')
563161692Sphk				usage("Invalid column number.");
564161692Sphk			if (column <= 0)
565161692Sphk				usage("Column number should be positive.");
566161692Sphk			break;
567118856Sphk		case 'c':
568118856Sphk			a = strtod(optarg, &p);
569118856Sphk			if (p != NULL && *p != '\0')
570118856Sphk				usage("Not a floating point number");
571118856Sphk			for (i = 0; i < NCONF; i++)
572118856Sphk				if (a == studentpct[i])
573118856Sphk					ci = i;
574118856Sphk			if (ci == -1)
575118856Sphk				usage("No support for confidence level");
576118856Sphk			break;
577161692Sphk		case 'd':
578161692Sphk			if (*optarg == '\0')
579161692Sphk				usage("Can't use empty delimiter string");
580161692Sphk			delim = optarg;
581161692Sphk			break;
582146689Srwatson		case 'n':
583146689Srwatson			flag_n = 1;
584146689Srwatson			break;
585121795Sphk		case 's':
586121795Sphk			flag_s = 1;
587121795Sphk			break;
588155894Smdodd		case 'w':
589155894Smdodd			termwidth = strtol(optarg, &p, 10);
590155894Smdodd			if (p != NULL && *p != '\0')
591155894Smdodd				usage("Invalid width, not a number.");
592155894Smdodd			if (termwidth < 0)
593155894Smdodd				usage("Unable to move beyond left margin.");
594155894Smdodd			break;
595118856Sphk		default:
596118856Sphk			usage("Unknown option");
597118856Sphk			break;
598118856Sphk		}
599118856Sphk	if (ci == -1)
600118856Sphk		ci = 2;
601118856Sphk	argc -= optind;
602118856Sphk	argv += optind;
603118856Sphk
604118856Sphk	if (argc == 0) {
605161692Sphk		ds[0] = ReadSet("-", column, delim);
606144993Smdodd		nds = 1;
607144993Smdodd	} else {
608144993Smdodd		if (argc > (MAX_DS - 1))
609144993Smdodd			usage("Too many datasets.");
610144993Smdodd		nds = argc;
611158246Sphk		for (i = 0; i < nds; i++)
612161692Sphk			ds[i] = ReadSet(argv[i], column, delim);
613118856Sphk	}
614118856Sphk
615158246Sphk	for (i = 0; i < nds; i++)
616158246Sphk		printf("%c %s\n", symbol[i+1], ds[i]->name);
617158246Sphk
618243079Seadler	if (!flag_n && !suppress_plot) {
619155894Smdodd		SetupPlot(termwidth, flag_s, nds);
620146689Srwatson		for (i = 0; i < nds; i++)
621146689Srwatson			DimPlot(ds[i]);
622146689Srwatson		for (i = 0; i < nds; i++)
623146689Srwatson			PlotSet(ds[i], i + 1);
624146689Srwatson		DumpPlot();
625146689Srwatson	}
626118856Sphk	VitalsHead();
627144993Smdodd	Vitals(ds[0], 1);
628144993Smdodd	for (i = 1; i < nds; i++) {
629144993Smdodd		Vitals(ds[i], i + 1);
630146689Srwatson		if (!flag_n)
631146689Srwatson			Relative(ds[i], ds[0], ci);
632118856Sphk	}
633118856Sphk	exit(0);
634118856Sphk}
635