summaryrefslogtreecommitdiff
path: root/support/regression/tests/bug-3129.c
blob: 7076c86a89e38c5c6177d091e45e53733255654f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
/*
   bug-3129.c
   Based on an error in the stdcbench self-test. 
 */

#include <testfwk.h>

#ifdef __SDCC
#pragma disable_warning 85
#endif

#if !defined(__SDCC_pdk14) && !defined(__SDCC_pdk15) && !defined(__SDCC_mcs51) && !defined(__SDCC_ds390) // Lack of memory
extern const char stdcbench_name_version_string[];

unsigned long stdcbench(void);

void stdcbench_error(const char *message);

union stdcbench_buffer
{
	unsigned char unsigned_char [1536];
	char basic_char [1536];
	signed int signed_int [32];
};

extern union stdcbench_buffer stdcbench_buffer;

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#include <stdbool.h>
#include <stdint.h>
typedef uint_least8_t node_t; // Needs to be an unsigned integer type, that has a maximum value >= MAX_N.
typedef uint_fast8_t count_t; // Needs to be an unsigned type, that has a maximum value >= 2^MAX_K.

#define MAX_K 4
#define MAX_N 8

bool ref_adjacency_matrix[MAX_N][MAX_N];
node_t ref_n;
node_t max_k;

bool check_lnlc(bool);


static bool adjacency_matrix[MAX_N][MAX_N];
static node_t n;
static node_t node_degrees[MAX_N];
static node_t degree_list[MAX_N];
static node_t num_edges;

static node_t k;
static node_t node_colors[MAX_N];

bool ref_adjacency_matrix[MAX_N][MAX_N];
node_t ref_n;
static node_t ref_node_degrees[MAX_N];
static node_t ref_degree_list[MAX_N];
static node_t ref_mindeg, ref_maxdeg;
static node_t ref_num_edges;
static node_t ref_neighbour_degrees[MAX_N];

node_t max_k;

static char *instructions; /* Should point to a buffer of size at least MAX_N^2 * 80 + MAX_N * MAX_K to get instructions or 0 if those are not needed. */

bool add(void);
bool recolor(void);
bool test(void);

/* Add a new, colored node, connect it to a subset of existing colors. */
bool add(void) __reentrant
{
	bool ret = false;
	node_t sum, ref_sum;
	node_t i;
	node_t new_node_color; /* Color of new node */
	count_t connect_colors; /* Colors new node is connected to (bitmask) */

	node_t node_degrees_backup[MAX_N];
	node_t degree_list_backup[MAX_N];
	node_t num_edges_backup;

	memcpy(node_degrees_backup, node_degrees, MAX_N * sizeof(node_t)); /* Copying fixed size is more efficient than copying only the part needed. */
	memcpy(degree_list_backup, degree_list, MAX_N * sizeof(node_t));
	num_edges_backup = num_edges;

	for(connect_colors = 0; connect_colors < (1 << k) && !ret; connect_colors++) /* New node can connect to any subset of existing colors. */
	{
		node_degrees[n] = 0;
		n++;
		memset(adjacency_matrix[n - 1], 0, (n - 1) * sizeof(bool)); /* Doing it once here is faster than having an else branch in the loop below. */

		/* Connect new node to existing nodes. */
		for(i = 0; i < n - 1; i++)
			if (connect_colors & (1 << node_colors[i]))
			{
				num_edges++;
				if(num_edges + (ref_n - n) * ref_mindeg / 2 > ref_num_edges) /* Early abort when there are too many edges already. */
 					goto tried;
				if(num_edges - num_edges_backup > ref_maxdeg) /* Early abort when there are too many edges at the new node already. */
					goto tried;

				adjacency_matrix[n - 1][i] = true;

				degree_list[node_degrees[i]]--;
				node_degrees[i]++;
				degree_list[node_degrees[i]]++;

				node_degrees[n - 1]++;
			}
		degree_list[node_degrees[n - 1]]++;

		if(num_edges + (ref_n - n) * ref_maxdeg < ref_num_edges) /* Early abort when there are too few edges still. */
			goto tried;

		if(n == ref_n)
		{
			if(test())
			{
				if(instructions)
				{
					instructions += sprintf(instructions, "Add node %d of color 0, connect it to nodes of the following colors: ", n - 1);
					for(i = 0; i < k; i++)
						if (connect_colors & (1 << i))
							instructions += sprintf(instructions, "%d ", i);
					instructions += sprintf(instructions, "\n");
				}

				ret = true;
			}
			goto tried;
		}

		/* Early abort when degrees are too high. */
		sum = 0;
		ref_sum = 0;
		for(i = ref_n - 1; i > 0; i--)
		{
			sum += degree_list[i];
			ref_sum += ref_degree_list[i];
			if(sum > ref_sum)
				goto tried;
		}

		for(new_node_color = 0; new_node_color <= k && new_node_color < max_k; new_node_color++) /* New node uses existing color, or exactly one above. */
		{
			node_t k_backup = k;

			if(new_node_color == k)
				k++;

			node_colors[n - 1] = new_node_color;

			/* Recurse to recoloring. */
			if(recolor())
			{
				if(instructions)
				{
					instructions += sprintf(instructions, "Add node %d of color %d, connect it to nodes of the following colors: ", n - 1, new_node_color);
					for(i = 0; i < k; i++)
						if (connect_colors & (1 << i))
							instructions += sprintf(instructions, "%d ", i);
					instructions +=  sprintf(instructions, "\n");
				}

				ret = true;

				goto tried;
			}

			k = k_backup;
		}

tried:
		n--;
		degree_list[n] = 0;
		memcpy(degree_list, degree_list_backup, MAX_N * sizeof(node_t));
		memcpy(node_degrees, node_degrees_backup, MAX_N * sizeof(node_t));
		num_edges = num_edges_backup;
	}

	return(ret);
}

static node_t recolormap[MAX_K];

bool do_recolor(void)  __reentrant
{
	bool ret = false;
	node_t i;
	node_t node_colors_backup[MAX_N];
	bool used_colors[MAX_N];
	node_t k_backup;

	/* Skip colorings that would recolor the most recently added node - it could have been added with target color in the first place instead. */
	if(recolormap[node_colors[n - 1]] != node_colors[n - 1] &&
		recolormap[node_colors[n - 1]] == recolormap[recolormap[node_colors[n - 1]]]) /* Recoloring the new node just for closing gaps created by recoloring of other nodes is ok. */
		return(false);

	k_backup = k;
	memcpy(node_colors_backup, node_colors, MAX_N * sizeof(node_t));

	/* Check coloring */
	memset(used_colors, 0, MAX_N * sizeof(bool));
	k = 0;
	for(i = 0; i < k_backup; i++)
	{
		used_colors[recolormap[i]] = true;
		if(recolormap[i] >= k)
			k = recolormap[i] + 1;
	}
	/* Do not allow gaps in colors. */
	for(i = 0; i < k; i++)
		if(!used_colors[i])
			goto tried;

	/* Recolor graph. */
	for(i = 0; i < n; i++)
		node_colors[i] = recolormap[node_colors[i]];

	/* Recurse to node addition. */
	if(ret = add())
	{
		for(i = 0; i < n; i++)
			if (node_colors[i] != node_colors_backup[i] && instructions)
				instructions += sprintf(instructions, "Recolor node %d from %d to %d\n", i, node_colors_backup[i], node_colors[i]);
	}

tried:
	memcpy(node_colors, node_colors_backup, MAX_N * sizeof(node_t));
	k = k_backup;

	return(ret);
}

bool maprecolor(node_t i)  __reentrant
{
	node_t j;

	if (i == k) /* Recurse in graph construction algorithm. */
		return(do_recolor());
	else /* Recurse in recoloring construction algorithm. */
		for(j = 0; j <= i; j++)	/* Never consider higher colors for recoloring. */
		{
			recolormap[i] = j;
			if(maprecolor(i + 1))
				return(true);
		}
	return(false);
}

/* Recolor nodes. */
bool recolor(void)  __reentrant
{
	return(maprecolor(0));
}

static node_t testperm[MAX_N];

/* Recursively generate permutations for isomorphism test. */
bool permtest(node_t i) __reentrant
{
	node_t j;

	/* Check that all edges to the most recently considered node are ok. */
	for(j = 0; j + 2 < i; j++)
		if((testperm[i - 1] > testperm[j] ? adjacency_matrix[testperm[i - 1]][testperm[j]] : adjacency_matrix[testperm[j]][testperm[i - 1]]) != ref_adjacency_matrix[i - 1][j])
			return(false);

	if(i == n)
		return(true);

	for(j = i; j < n; j++)
	{
		node_t t;

		if (node_degrees[testperm[j]] != ref_node_degrees[i]) /* Do not try permutations where degrees do not match. */
			continue;

		t = testperm[i];
		testperm[i] = testperm[j];
		testperm[j] = t;

		if(permtest(i + 1))
			return(true);

		t = testperm[i];
		testperm[i] = testperm[j];
		testperm[j] = t;
	}

	return(false);
}

int cmp(const void *l, const void *r) __reentrant
{
	return *((node_t*)r) - *((node_t *)l);
}

void calc_neighbour_degrees(node_t *restrict neighbour_degrees, bool (*adjacency_matrix)[MAX_N], const node_t *restrict degrees)
{
	node_t i, j;

	memset(neighbour_degrees, 0, MAX_N);
	for(i = 0; i < ref_n; i++)
		for(j = 0; j < i; j++)
			if(adjacency_matrix[i][j])
			{
				neighbour_degrees[i] += (1 << (degrees[j] - 1));
				neighbour_degrees[j] += (1 << (degrees[i] - 1));
			}
	qsort(neighbour_degrees, ref_n, sizeof(node_t), cmp);
}

/* Isomorphism test */
bool test(void)
{
	node_t i;
	node_t neighbour_degrees[MAX_N];

	/* Compare degree list first. */
	if (memcmp(ref_degree_list, degree_list, n * sizeof(node_t)))
		return(false);

	/* Compare degrees of neighbours next. */
	calc_neighbour_degrees(neighbour_degrees, adjacency_matrix, node_degrees);
	if (memcmp(ref_neighbour_degrees, neighbour_degrees, n * sizeof(node_t)))
		return(false);

	for(i = 0; i < n; i++)
		testperm[i] = i;

	return(permtest(0));
}


/* Has the graph ref_adjacency_matrix of ref_n nodes linear nlc-width at most k? */
bool check_lnlc(bool output_instructions)
{
	bool ret;
	char *startinstructions;
	char *outinstructions = stdcbench_buffer.basic_char;
	node_t i, j;

	if(!ref_n)
		return(true);

	memset(ref_node_degrees, 0, MAX_N * sizeof(node_t));
	memset(ref_degree_list + 1, 0, (MAX_N - 1) * sizeof(node_t));
	ref_degree_list[0] = ref_n;
	ref_num_edges = 0;
	for(i = 0; i < ref_n; i++)
		for(j = 0; j < i; j++)
			if(ref_adjacency_matrix[i][j])
			{
				ref_degree_list[ref_node_degrees[i]]--;
				ref_node_degrees[i]++;
				ref_degree_list[ref_node_degrees[i]]++;
				ref_degree_list[ref_node_degrees[j]]--;
				ref_node_degrees[j]++;
				ref_degree_list[ref_node_degrees[j]]++;
				ref_num_edges++;
			}

	for(i = 1, ref_mindeg = ref_maxdeg = ref_node_degrees[0]; i < ref_n; i++)
	{
		node_t ref_deg = ref_node_degrees[i];
		if (ref_deg < ref_mindeg)
			ref_mindeg = ref_deg;
		if (ref_deg > ref_maxdeg)
			ref_maxdeg = ref_deg;
	}

	calc_neighbour_degrees(ref_neighbour_degrees, ref_adjacency_matrix, ref_node_degrees);

	memset(degree_list, 0, MAX_N * sizeof(node_t));
	num_edges = 0;
	k = 0;

	if(output_instructions)
	{
		if(!(startinstructions = instructions = malloc (60 + (ref_n) * (72 + max_k / 8 * 2) + (ref_n - 1) * (ref_n - 2) / 2 * 28)))
			stdcbench_error("c90lib c90lib_lnlc(): malloc() failed\n");
		else
			startinstructions[0] = 0;
	}
	else
		startinstructions = instructions = 0;
	
	ret = add();

	if (ret && startinstructions)
	{
		char *c;

		outinstructions += sprintf(outinstructions, "Instructions for constructing the graph:");

		while(c = strrchr(startinstructions, '\n'))
		{
			outinstructions += sprintf(outinstructions, c);
			*c = 0;
		}
		outinstructions += sprintf(outinstructions, "\n%s\n", startinstructions);
	}

	free(startinstructions);

	return(ret);
}

static const char resultinstructions[] =
	"Instructions for constructing the graph:\n"
	"\n"
	"Add node 0 of color 0, connect it to nodes of the following colors: \n"
	"Add node 1 of color 1, connect it to nodes of the following colors: \n"
	"Add node 2 of color 2, connect it to nodes of the following colors: 0 1 \n"
	"Add node 3 of color 1, connect it to nodes of the following colors: 0 \n"
	"Add node 4 of color 0, connect it to nodes of the following colors: 0 1 \n"
	"Recolor node 2 from 2 to 1\n"
	"Add node 5 of color 0, connect it to nodes of the following colors: 1 \n";

static volatile const bool prism[6][6] =
	{{0, 1, 1, 1, 0, 0},
	{1, 0, 1, 0, 1, 0},
	{1, 1, 0, 0, 0, 1},
	{1, 0, 0, 0, 1, 1},
	{0, 1, 0, 1, 0, 1},
	{0, 0, 1, 1, 1, 0}};

void c90lib_lnlc(void)
{
	node_t i, j;

	ref_n = 6;
	for(i = 0; i < ref_n; i++)
		for(j = 0; j < ref_n; j++)
			ref_adjacency_matrix[i][j] = prism[i][j];

	for(max_k = 0; max_k <= MAX_K; max_k++)
		if(check_lnlc(true))
			break;

	if(k != 1 || strcmp(stdcbench_buffer.basic_char, resultinstructions))
		stdcbench_error("c90lib c90lib_lnlc(): Result validation failed");
}

#define REG(addr, reg)	__sfr __at(addr) reg

void stdcbench_error(const char *message)
{
	ASSERT(0);
}

extern void c90lib_lnlc(void);

union stdcbench_buffer stdcbench_buffer;

const char stdcbench_name_version_string[] = "stdcbench 0.6";
#endif

void
testBug(void)
{
#if !defined(__SDCC_mos6502) && !defined(__SDCC_mos65c02) // insufficient stack space
#if !defined(__SDCC_pdk14) && !defined(__SDCC_pdk15) && !defined(__SDCC_mcs51) && !defined(__SDCC_ds390) // Lack of memory
	c90lib_lnlc();
#endif
#endif
}