lb.c419 lines · 11.0 KB · raw
1#include "lb.h"
2#include "bm.h"
3#include <assert.h>
4#include <string.h>
5#ifdef LB_DEBUG
6#include <stdio.h>
7#endif
8
9void
10lb_init(struct lb * lb, size_t n) {
11    assert(n <= LB_SIZE);
12    assert(n > 0);
13    memset(lb, 0, sizeof(*lb));
14    lb->size = n;
15    lb->dirty = true;
16    for (size_t x = 0; x < lb->size; x++) {
17        lb->x_root[x] = x;
18        lb->x_has_equivs[x] = false;
19        BM_SET(lb->x_equivs[x], x);
20    }
21}
22
23void
24lb_init_equivalent(struct lb * lb, size_t x0, size_t x1) {
25    assert(x0 < x1);
26    assert(x1 < lb->size);
27    assert(lb->x_root[x0] == x0);
28    assert(!lb->x_has_equivs[x1]);
29    assert(!lb->marked);
30    if (lb->x_root[x1] == x0) return;
31    assert(lb->x_root[x1] == x1);
32
33    lb->x_root[x1] = x0;
34    BM_SET(lb->x_equivs[x0], x1);
35    lb->x_has_equivs[x0] = true;
36}
37
38static void
39lb_mark_positive_half(struct lb * lb, bool h, size_t x, size_t y) {
40    assert(lb->halfs[h].pair[x] == 0);
41    for (size_t i = 0; i < lb->size; i++) {
42        if (i != x) {
43            BM_SET(lb->halfs[h].bs[i], y);
44        }
45        if (i != y) {
46            BM_SET(lb->halfs[h].bs[x], i);
47        }
48    }
49    BM_SET(lb->halfs[h].paired, x);
50    lb->halfs[h].pair[x] = y;
51    lb->dirty = true;
52}
53
54void
55lb_mark_positive(struct lb * lb, size_t x, size_t y) {
56    assert(x < lb->size);
57    assert(y < lb->size);
58
59    if (lb->x_has_equivs[x]) {
60        size_t x_original = x;
61        while (BM_ISSET(lb->halfs[0].paired, x)) {
62            x++;
63            assert(x < lb->size && lb->x_root[x] == x_original);
64        }
65    }
66
67    assert(!BM_ISSET(lb->halfs[0].bs[x], y));
68    assert(!BM_ISSET(lb->halfs[1].bs[y], x));
69    if (BM_ISSET(lb->halfs[1].paired, y)) {
70        assert(BM_ISSET(lb->halfs[1].paired, y));
71        assert(lb->halfs[0].pair[x] == y);
72        assert(lb->halfs[1].pair[y] == x);
73        return;
74    }
75    assert(!BM_ISSET(lb->halfs[1].paired, y));
76
77    for (size_t i = lb->x_root[x]; i < lb->size; i++) {
78        if (!BM_ISSET(lb->x_equivs[x], i)) {
79            continue;
80        }
81        if (BM_ISSET(lb->halfs[0].paired, i)) {
82            continue;
83        }
84        lb_mark_positive_half(lb, 0, i, y);
85        lb_mark_positive_half(lb, 1, y, i);
86        return;
87    }
88    assert(0);
89}
90
91static void
92lb_mark_negative_half(struct lb * lb, bool h, size_t x, size_t y) {
93    if (BM_ISSET(lb->halfs[h].paired, x)) {
94        assert(lb->halfs[h].pair[x] != y);
95    }
96    if (!BM_ISSET(lb->halfs[h].bs[x], y)) {
97        BM_SET(lb->halfs[h].bs[x], y);
98        lb->dirty = true;
99    }
100}
101
102void
103lb_mark_negative(struct lb * lb, size_t x, size_t y) {
104    assert(x < lb->size);
105    assert(y < lb->size);
106    for (size_t i = lb->x_root[x]; i < lb->size; i++) {
107        if (!BM_ISSET(lb->x_equivs[x], i)) {
108            continue;
109        }
110        lb_mark_negative_half(lb, 0, i, y);
111        lb_mark_negative_half(lb, 1, y, i);
112        if (!lb->x_has_equivs[x]) {
113            break;
114        }
115    }
116}
117
118static void
119lb_deduce_unique(struct lb * lb, bool h) {
120    // Look for cases where there is only 1 possibility left
121    for (size_t i = 0; i < lb->size; i++) {
122        if (BM_ISSET(lb->halfs[h].paired, i)) {
123            continue;
124        }
125        size_t p = BM_POPCOUNT(lb->halfs[h].bs[i]);
126        assert(p < lb->size);
127        if (p == lb->size - 1) {
128            size_t z = BM_FFZ(lb->halfs[h].bs[i]);
129            assert(z < lb->size);
130            lb_mark_positive_half(lb, h, i, z);
131            lb_mark_positive_half(lb, !h, z, i);
132        }
133    }
134}
135
136static void
137lb_deduce_clique(struct lb * lb, bool h) {
138    // Look for self-contained subsets
139    size_t l = 0;
140    for (size_t i = 0; i < lb->size; i++) {
141        if (BM_ISSET(lb->halfs[h].paired, i)) {
142            continue;
143        }
144        size_t p = lb->size - BM_POPCOUNT(lb->halfs[h].bs[i]);
145        if (p <= lb->size / 2) {
146            uint64_t js[LB_BM_SIZE];
147            memset(js, 0xFF, sizeof(js));
148            for (size_t j = 0; j < lb->size; j++) {
149                if (!BM_ISSET(lb->halfs[h].bs[i], j)) {
150                    continue;
151                }
152                BM_ANDEQ(js, lb->halfs[!h].bs[j]);
153            }
154            size_t q = BM_POPCOUNT(js);
155            assert(q <= p); // Hill's Criteria
156            if (q == p) {
157                // Found a clique
158                for (size_t x = 0; x < lb->size; x++) {
159                    if (BM_ISSET(js, x)) {
160                        continue;
161                    }
162                    for (size_t y = 0; y < lb->size; y++) {
163                        if (BM_ISSET(lb->halfs[h].bs[i], y)) {
164                            continue;
165                        }
166                        lb_mark_negative_half(lb, h, x, y);
167                        lb_mark_negative_half(lb, !h, y, x);
168                    }
169                }
170                return;
171            }
172        }
173    }
174}
175
176static void
177lb_deduce_equivalents(struct lb * lb) {
178    for (size_t x = 0; x < lb->size; x++) {
179        if (!lb->x_has_equivs[x]) {
180            continue;
181        }
182
183        uint64_t bs_union[LB_BM_SIZE];
184        size_t union_count = 0;
185        for (size_t i = lb->x_root[x]; i < lb->size; i++) {
186            if (!BM_ISSET(lb->x_equivs[x], i)) {
187                continue;
188            }
189            if (BM_ISSET(lb->halfs[0].paired, i)) {
190                continue;
191            }
192            if (union_count == 0) {
193                memcpy(bs_union, lb->halfs[0].bs[i], sizeof(bs_union));
194            } else {
195                BM_ANDEQ(bs_union, lb->halfs[0].bs[i]);
196            }
197            union_count++;
198        }
199        if (union_count < 2) {
200            // Would be handled by lb_deduce_unique
201            continue;
202        }
203        size_t c = lb->size - BM_POPCOUNT(bs_union);
204        assert(c >= union_count);
205        if (c == union_count) {
206            for (size_t i = lb->x_root[x]; i < lb->size; i++) {
207                if (BM_ISSET(bs_union, i)) {
208                    continue;
209                }
210                if (BM_ISSET(lb->halfs[0].paired, i)) {
211                    continue;
212                }
213                lb_mark_positive(lb, i, BM_FFZ(lb->halfs[0].bs[i]));
214            }
215            
216        }
217    }
218}
219
220void
221lb_deduce(struct lb * lb) {
222    while (lb->dirty) {
223        lb->dirty = false;
224
225        lb_deduce_unique(lb, 0);
226        if (lb->dirty) continue;
227        lb_deduce_unique(lb, 1);
228        if (lb->dirty) continue;
229
230        lb_deduce_clique(lb, 0);
231        if (lb->dirty) continue;
232        lb_deduce_clique(lb, 1);
233        if (lb->dirty) continue;
234
235        lb_deduce_equivalents(lb);
236        if (lb->dirty) continue;
237    }
238}
239
240#ifdef LB_DEBUG
241static void
242lb_selftest_print(struct lb * lb, const char * name) {
243    if (name != NULL) {
244        printf(">>> %s\n", name);
245        lb_deduce(lb);
246    }
247
248    printf("     ");
249    for (size_t i = 0; i < lb->size; i++) {
250        printf("%c", 'A' + (char) (i % 26));
251    }
252    printf("     ");
253    for (size_t i = 0; i < lb->size; i++) {
254        printf("%c", '0' + (char) (i % 10));
255    }
256    printf("\n");
257
258    for (size_t i = 0; i < lb->size; i++) {
259        for (int h = 0; h < 2; h++) {
260            if (h == 0) {
261                printf("   %c ", '0' + (char) (i % 10));
262            } else {
263                printf("   %c ", 'A' + (char) (i % 26));
264            }
265            for (size_t j = 0; j < lb->size; j++) {
266                if (BM_ISSET(lb->halfs[h].bs[i], j)) {
267                    printf("x");
268                } else if (BM_ISSET(lb->halfs[h].paired, i)) {
269                    assert(lb->halfs[h].pair[i] == j);
270                    printf("@");
271                } else {
272                    printf(".");
273                }
274            }
275        }
276        printf("\n");
277    }
278    printf("\n");
279}
280
281void
282lb_selftest() {
283    struct lb lb[1];
284    lb_init(lb, 6);
285    lb_selftest_print(lb, "init");
286
287    lb_mark_negative(lb, 0, 1);
288    lb_selftest_print(lb, "-A1");
289
290    lb_mark_positive(lb, 2, 3);
291    lb_selftest_print(lb, "+C3");
292
293    lb_mark_negative(lb, 0, 2);
294    lb_mark_negative(lb, 0, 4);
295    lb_mark_negative(lb, 0, 5);
296    lb_selftest_print(lb, "+A0");
297
298    lb_mark_positive(lb, 1, 1);
299    lb_mark_positive(lb, 3, 5);
300    lb_mark_negative(lb, 4, 4);
301    lb_selftest_print(lb, "Finish");
302
303    lb_init(lb, 6);
304    lb_mark_negative(lb, 2, 0);
305    lb_mark_negative(lb, 2, 1);
306    lb_mark_negative(lb, 3, 0);
307    lb_mark_negative(lb, 3, 1);
308    lb_mark_negative(lb, 4, 0);
309    lb_mark_negative(lb, 4, 1);
310    lb_mark_negative(lb, 5, 0);
311    lb_mark_negative(lb, 5, 1);
312    lb_selftest_print(lb, "Clique 2x4");
313
314    lb_init(lb, 6);
315    lb_mark_negative(lb, 3, 0);
316    lb_mark_negative(lb, 3, 1);
317    lb_mark_negative(lb, 3, 2);
318    lb_mark_negative(lb, 4, 0);
319    lb_mark_negative(lb, 4, 1);
320    lb_mark_negative(lb, 4, 2);
321    lb_mark_negative(lb, 5, 0);
322    lb_mark_negative(lb, 5, 1);
323    lb_mark_negative(lb, 5, 2);
324    lb_mark_negative(lb, 2, 0);
325    lb_mark_negative(lb, 2, 1);
326    lb_selftest_print(lb, "Clique 3x3");
327
328    lb_init(lb, 6);
329    for (size_t i = 2; i < lb->size; i++) {
330        for (size_t j = 0; j < i && j < 4; j++) {
331            lb_mark_negative(lb, j, i);
332        }
333    }
334    lb_selftest_print(lb, NULL);
335    lb_selftest_print(lb, "diagonal on 6");
336
337    lb_mark_negative(lb, 0, 0);
338    lb_mark_negative(lb, 4, 4);
339    lb_selftest_print(lb, "full solve on 6");
340
341    // 5x5 tests
342    lb_init(lb, 5);
343    lb_mark_negative(lb, 2, 0);
344    lb_mark_negative(lb, 2, 1);
345    lb_mark_negative(lb, 3, 0);
346    lb_mark_negative(lb, 3, 1);
347    lb_mark_negative(lb, 4, 0);
348    lb_mark_negative(lb, 4, 1);
349    lb_selftest_print(lb, "Clique 2x3");
350
351    lb_init(lb, 5);
352    for (size_t i = 2; i < lb->size; i++) {
353        for (size_t j = 0; j < i && j < 4; j++) {
354            lb_mark_negative(lb, j, i);
355        }
356    }
357    lb_selftest_print(lb, NULL);
358    lb_selftest_print(lb, "diagonal on 5");
359
360    lb_mark_negative(lb, 0, 0);
361    lb_selftest_print(lb, "full solve on 5");
362
363    // 6x6 with equivs
364    lb_init(lb, 6);
365    lb_init_equivalent(lb, 0, 1);
366    lb_init_equivalent(lb, 0, 2);
367    lb_init_equivalent(lb, 0, 3);
368    lb_init_equivalent(lb, 0, 4);
369    lb_selftest_print(lb, "Init");
370
371    lb_mark_negative(lb, 0, 1);
372    lb_selftest_print(lb, "Solved");
373
374    lb_init(lb, 6);
375    lb_init_equivalent(lb, 0, 1);
376    lb_init_equivalent(lb, 0, 2);
377    lb_init_equivalent(lb, 0, 3);
378    lb_init_equivalent(lb, 0, 4);
379    lb_init_equivalent(lb, 0, 5);
380    lb_selftest_print(lb, "Fully solved");
381
382    // 6x6, replicate CT 2 key issue
383    lb_init(lb, 6);
384    lb_init_equivalent(lb, 0, 1);
385    lb_selftest_print(lb, "Init");
386    
387    lb_mark_negative(lb, 0, 2);
388    lb_mark_negative(lb, 0, 3);
389    lb_mark_negative(lb, 0, 4);
390    lb_mark_negative(lb, 0, 5);
391    lb_selftest_print(lb, "Step 2");
392
393    // 5x5, mark equivalents positive
394    lb_init(lb, 5);
395    lb_init_equivalent(lb, 0, 1);
396    lb_init_equivalent(lb, 0, 2);
397    lb_selftest_print(lb, "Init");
398
399    lb_mark_positive(lb, 0, 0);
400    lb_mark_positive(lb, 1, 1);
401    lb_selftest_print(lb, "Step 2");
402
403    lb_mark_positive(lb, 0, 2);
404    lb_selftest_print(lb, "Step 3");
405
406    return;
407
408    // 256x256 test
409    lb_init(lb, 256);
410    for (size_t i = 3; i < lb->size; i++) {
411        for (size_t j = 0; j < i && j < lb->size - 3; j++) {
412            lb_mark_negative(lb, j, i);
413        }
414    }
415    lb_deduce(lb);
416    //lb_selftest_print(lb, "diagonal on 256x256");
417}
418
419#endif