summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormillert <>2017-05-17 14:47:06 +0000
committermillert <>2017-05-17 14:47:06 +0000
commitae5a5006e37f7a3e29b7e75e240e83cd1b3f87ae (patch)
treefd4d13b3763bf22368228db1f1cb4307d5032aba /src
parentcca2399941eaa626992a0ec066ba0ec955059bb4 (diff)
downloadopenbsd-ae5a5006e37f7a3e29b7e75e240e83cd1b3f87ae.tar.gz
openbsd-ae5a5006e37f7a3e29b7e75e240e83cd1b3f87ae.tar.bz2
openbsd-ae5a5006e37f7a3e29b7e75e240e83cd1b3f87ae.zip
Add qsort(3) regress based on Bentley & McIlroy's "Engineering a Sort Function"
Diffstat (limited to 'src')
-rw-r--r--src/regress/lib/libc/Makefile4
-rw-r--r--src/regress/lib/libc/qsort/Makefile14
-rw-r--r--src/regress/lib/libc/qsort/qsort_test.c262
3 files changed, 278 insertions, 2 deletions
diff --git a/src/regress/lib/libc/Makefile b/src/regress/lib/libc/Makefile
index b9c3dfec85..f84b991fa4 100644
--- a/src/regress/lib/libc/Makefile
+++ b/src/regress/lib/libc/Makefile
@@ -1,10 +1,10 @@
1# $OpenBSD: Makefile,v 1.48 2016/05/29 17:09:07 beck Exp $ 1# $OpenBSD: Makefile,v 1.49 2017/05/17 14:47:06 millert Exp $
2 2
3SUBDIR+= _setjmp alloca arc4random-fork 3SUBDIR+= _setjmp alloca arc4random-fork
4SUBDIR+= atexit basename cephes cxa-atexit db dirname env 4SUBDIR+= atexit basename cephes cxa-atexit db dirname env
5SUBDIR+= explicit_bzero fmemopen fnmatch fpclassify getcap getopt_long glob 5SUBDIR+= explicit_bzero fmemopen fnmatch fpclassify getcap getopt_long glob
6SUBDIR+= hsearch ifnameindex longjmp locale malloc mkstemp modf netdb 6SUBDIR+= hsearch ifnameindex longjmp locale malloc mkstemp modf netdb
7SUBDIR+= open_memstream orientation popen printf 7SUBDIR+= open_memstream orientation popen printf qsort
8SUBDIR+= regex setjmp setjmp-signal sigsetjmp sprintf stdio_threading 8SUBDIR+= regex setjmp setjmp-signal sigsetjmp sprintf stdio_threading
9SUBDIR+= stpncpy strerror strlcat strlcpy strnlen strtod strtol strtonum 9SUBDIR+= stpncpy strerror strlcat strlcpy strnlen strtod strtol strtonum
10SUBDIR+= telldir time timingsafe vis 10SUBDIR+= telldir time timingsafe vis
diff --git a/src/regress/lib/libc/qsort/Makefile b/src/regress/lib/libc/qsort/Makefile
new file mode 100644
index 0000000000..d3c4e2baa2
--- /dev/null
+++ b/src/regress/lib/libc/qsort/Makefile
@@ -0,0 +1,14 @@
1# $OpenBSD: Makefile,v 1.1 2017/05/17 14:47:06 millert Exp $
2
3PROG= qsort_test
4CFLAGS+=-Wall
5LDADD+= -lm
6DPADD+= ${LIBM}
7
8qsort-regress: ${PROG}
9 ./${PROG}
10
11REGRESS_TARGETS=qsort-regress
12.PHONY: ${REGRESS_TARGETS}
13
14.include <bsd.regress.mk>
diff --git a/src/regress/lib/libc/qsort/qsort_test.c b/src/regress/lib/libc/qsort/qsort_test.c
new file mode 100644
index 0000000000..4b2100de97
--- /dev/null
+++ b/src/regress/lib/libc/qsort/qsort_test.c
@@ -0,0 +1,262 @@
1/*
2 * Copyright (c) 2017 Todd C. Miller <Todd.Miller@courtesan.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <setjmp.h>
21#include <math.h>
22#include <err.h>
23
24/*
25 * Test program based on Bentley & McIlroy's "Engineering a Sort Function".
26 * Uses heapsort(3) to check the results.
27 */
28
29enum distribution {
30 SAWTOOTH,
31 RAND,
32 STAGGER,
33 PLATEAU,
34 SHUFFLE,
35 INVALID
36};
37
38#define min(a, b) (((a) < (b)) ? (a) : (b))
39
40static size_t compares;
41static size_t max_compares;
42static size_t abrt_compares;
43static sigjmp_buf cmpjmp;
44
45static int
46cmp(const void *v1, const void *v2)
47{
48 const int a = *(const int *)v1;
49 const int b = *(const int *)v2;
50
51 return a > b ? 1 : a < b ? -1 : 0;
52}
53
54static int
55cmp_checked(const void *v1, const void *v2)
56{
57 const int a = *(const int *)v1;
58 const int b = *(const int *)v2;
59
60 compares++;
61 if (compares > abrt_compares)
62 siglongjmp(cmpjmp, 1);
63 return a > b ? 1 : a < b ? -1 : 0;
64}
65
66static int
67check_result(char *prefix, int *got, int *expected, enum distribution dist,
68 int m, int n)
69{
70 int i;
71
72 if (compares > max_compares) {
73 warnx("%s: %zu compares, max %zu, dist %d, m: %d, n: %d",
74 prefix, compares, max_compares, dist, m, n);
75 }
76
77 for (i = 0; i < n; i++) {
78 if (got[i] != expected[i]) {
79 warnx("%s: failure at %d, dist %d, m: %d, n: %d",
80 prefix, i, dist, m, n);
81 return 1;
82 }
83 }
84 return 0;
85}
86
87static void
88fill_test_array(int *x, int n, int dist, int m)
89{
90 int i, j, k;
91
92 for (i = 0, j = 0, k = 1; i < n; i++) {
93 switch (dist) {
94 case SAWTOOTH:
95 x[i] = i % m;
96 break;
97 case RAND:
98 x[i] = arc4random_uniform(m);
99 break;
100 case STAGGER:
101 x[i] = (i * m + i) % n;
102 break;
103 case PLATEAU:
104 x[i] = min(i, m);
105 break;
106 case SHUFFLE:
107 x[i] = arc4random_uniform(m) ? (j += 2) : (k += 2);
108 break;
109 default:
110 err(1, "unexpected distribution %d", dist);
111 }
112 }
113}
114
115static int
116test_distribution(int dist, int m, int n, int *x, int *y, int *z)
117{
118 int i, j;
119 int errors = 0;
120
121 /* Fill in x[] based on distribution and 'm' */
122 fill_test_array(x, n, dist, m);
123
124 /* Test on copy of x[] */
125 for (i = 0; i < n; i++)
126 y[i] = z[i] = x[i];
127 compares = 0;
128 if (sigsetjmp(cmpjmp, 1) != 0) {
129 warnx("qsort aborted: %zu compares, dist %d, m: %d, n: %d",
130 compares, dist, m, n);
131 errors++;
132 } else {
133 qsort(y, n, sizeof(y[0]), cmp_checked);
134 heapsort(z, n, sizeof(z[0]), cmp);
135 errors += check_result("copy", y, z, dist, m, n);
136 }
137
138 /* Test on reversed copy of x[] */
139 for (i = 0, j = n - 1; i < n; i++, j--)
140 y[i] = z[i] = x[j];
141 compares = 0;
142 if (sigsetjmp(cmpjmp, 1) != 0) {
143 warnx("qsort aborted (%s): %zu compares, dist %d, m: %d, n: %d",
144 "reversed", compares, dist, m, n);
145 errors++;
146 } else {
147 qsort(y, n, sizeof(y[0]), cmp_checked);
148 heapsort(z, n, sizeof(z[0]), cmp);
149 errors += check_result("reversed", y, z, dist, m, n);
150 }
151
152 /* Test with front half of x[] reversed */
153 for (i = 0, j = (n / 2) - 1; i < n / 2; i++, j--)
154 y[i] = z[i] = x[j];
155 for (; i < n; i++)
156 y[i] = z[i] = x[i];
157 compares = 0;
158 if (sigsetjmp(cmpjmp, 1) != 0) {
159 warnx("qsort aborted (%s): %zu compares, dist %d, m: %d, n: %d",
160 "front reversed", compares, dist, m, n);
161 errors++;
162 } else {
163 qsort(y, n, sizeof(y[0]), cmp_checked);
164 heapsort(z, n, sizeof(z[0]), cmp);
165 errors += check_result("front reversed", y, z, dist, m, n);
166 }
167
168 /* Test with back half of x[] reversed */
169 for (i = 0; i < n / 2; i++)
170 y[i] = z[i] = x[i];
171 for (j = n - 1; i < n; i++, j--)
172 y[i] = z[i] = x[j];
173 compares = 0;
174 if (sigsetjmp(cmpjmp, 1) != 0) {
175 warnx("qsort aborted (%s): %zu compares, dist %d, m: %d, n: %d",
176 "back reversed", compares, dist, m, n);
177 errors++;
178 } else {
179 qsort(y, n, sizeof(y[0]), cmp_checked);
180 heapsort(z, n, sizeof(z[0]), cmp);
181 errors += check_result("back reversed", y, z, dist, m, n);
182 }
183
184 /* Test on sorted copy of x[] */
185 heapsort(x, n, sizeof(x[0]), cmp);
186 for (i = 0; i < n; i++)
187 y[i] = x[i];
188 compares = 0;
189 if (sigsetjmp(cmpjmp, 1) != 0) {
190 warnx("qsort aborted (%s): %zu compares, dist %d, m: %d, n: %d",
191 "sorted", compares, dist, m, n);
192 errors++;
193 } else {
194 qsort(y, n, sizeof(y[0]), cmp_checked);
195 errors += check_result("sorted", y, x, dist, m, n);
196 }
197
198 /* Test with i%5 added to x[i] (dither) */
199 for (i = 0, j = n - 1; i < n; i++, j--)
200 y[i] = z[i] = x[j] + (i % 5);
201 compares = 0;
202 if (sigsetjmp(cmpjmp, 1) != 0) {
203 warnx("qsort aborted (%s): %zu compares, dist %d, m: %d, n: %d",
204 "dithered", compares, dist, m, n);
205 errors++;
206 } else {
207 qsort(y, n, sizeof(y[0]), cmp_checked);
208 heapsort(z, n, sizeof(z[0]), cmp);
209 errors += check_result("dithered", y, z, dist, m, n);
210 }
211
212 return errors;
213}
214
215static int
216run_tests(int m, int n)
217{
218 int *x, *y, *z;
219 int errors = 0;
220 double nlgn;
221 enum distribution dist;
222
223 /*
224 * The expected number of compares is A * n * log2(n)
225 * where A is between 1 and 2. If A is greater than 1.5
226 * we'll print a warning. If A is greater than 10 we
227 * abort the test since qsort has probably gone quadratic.
228 */
229 nlgn = n * log2(n);
230 max_compares = nlgn * 1.5;
231 abrt_compares = nlgn * 10;
232
233 x = reallocarray(NULL, n, sizeof(x[0]));
234 y = reallocarray(NULL, n, sizeof(y[0]));
235 z = reallocarray(NULL, n, sizeof(z[0]));
236 if (y == NULL || y == NULL || z == NULL)
237 err(1, NULL);
238
239 for (dist = SAWTOOTH; dist != INVALID; dist++)
240 errors += test_distribution(dist, m, n, x, y, z);
241
242 free(x);
243 free(y);
244 free(z);
245 return errors;
246}
247
248int
249main(int argc, char *argv[])
250{
251 int *nn, nums[] = { 100, 1023, 1024, 1025, 4095, 4096, 4097, -1 };
252 int m, n;
253 int errors = 0;
254
255 for (nn = nums; (n = *nn) > 0; nn++) {
256 for (m = 1; m < 2 * n; m *= 2) {
257 errors += run_tests(m, n);
258 }
259 }
260
261 return errors;
262}