Fix false positive in bfzIII word detection
[clav.git] / mutation-finder.c
blob661efb054e30c869044e780c6df3e729eff89158
1 /*
2 * Copyright (c) 2016-2019, S. Gilles <sgilles@math.umd.edu>
4 * Permission to use, copy, modify, and/or distribute this software
5 * for any purpose with or without fee is hereby granted, provided
6 * that the above copyright notice and this permission notice appear
7 * in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <errno.h>
19 #include <limits.h>
20 #include <locale.h>
21 #include <signal.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
28 #include "macros.h"
29 #include "quiver.h"
31 /* Flag for whether to print info */
32 static volatile sig_atomic_t status_requested = 0;
34 /* Signal handler */
35 static void handle(int sig);
37 /* Signal handler */
38 static void
39 handle(int sig)
41 /* XXX: I'd like to check for SIGINFO here */
42 if (sig == SIGUSR1) {
43 status_requested = 1;
44 signal(sig, handle);
48 /* Print usage */
49 static void
50 usage(char *argv0)
52 printf("Usage: %s -s <startfile> -e <endfile>\n", argv0);
53 printf(" [ -v ] # Be verbose\n");
54 printf(" [ -R ] # Require all non-frozen in sequence\n");
55 printf(" [ -H ] # Test via graph heuristics, not exactness\n");
56 printf(" [ -c <edgecap> ]\n");
57 printf(" [ -f <frozen> [ -f <frozen> [...] ] ]\n");
58 printf(" [ -l <startlen> ] [ -m <maxlen> ]\n");
59 printf("\n");
60 printf(" startfile: a quiver file representing the start "
61 "state\n");
62 printf(" endfile: a quiver file representing the desired end "
63 "state\n");
64 printf(" edgecap: ignore mutations if the cause edges with"
65 "weights above this\n");
66 printf(" startlen: start with sequences of this length\n");
67 printf(" maxlen: halt after exhausting sequences of this "
68 "length\n");
69 printf(" frozen: a vertex with this name will not be "
70 "mutated\n");
73 static int
74 cmp(const void *a, const void *b)
76 struct vertex **va = (struct vertex **) a;
77 struct vertex **vb = (struct vertex **) b;
79 return strcmp((*va)->name, (*vb)->name);
82 /* This one just counts the number of edges */
83 static uint_fast8_t
84 dumb_checksum1(struct quiver const * const q)
86 uint_fast8_t c = 0;
87 struct rational *r = 0;
88 struct rational *s = 0;
90 for (size_t j = 0; j < q->v_num; ++j) {
91 for (size_t k = 0; k < j; ++k) {
92 r = &q->e[j * q->v_len + k];
93 s = &q->e[k * q->v_len + j];
95 if (!!(r->p ||
96 s->p)) {
97 c++;
102 return c;
105 /* This one counts the number of each p and q */
106 static uint64_t
107 dumb_checksum2(struct quiver const * const q)
109 uint64_t c = 0;
110 struct rational *r = 0;
111 struct rational *s = 0;
112 size_t by_p[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
113 size_t by_q[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
115 for (size_t j = 0; j < q->v_num; ++j) {
116 for (size_t k = 0; k < q->v_num; ++k) {
117 r = &q->e[j * q->v_len + k];
118 s = &q->e[k * q->v_len + j];
119 by_p[(((4 + r->p) % 8) + 8) % 8]++;
120 by_q[r->q % 8]++;
121 by_p[(((4 + s->p) % 8) + 8) % 8]++;
122 by_q[s->q % 8]++;
126 c = 8 * 0 + by_p[0] + 4 * by_q[0];
127 c = 8 * c + by_p[1] + 4 * by_q[1];
128 c = 8 * c + by_p[2] + 4 * by_q[2];
129 c = 8 * c + by_p[3] + 4 * by_q[3];
130 c = 8 * c + by_p[4] + 4 * by_q[4];
131 c = 8 * c + by_p[5] + 4 * by_q[5];
132 c = 8 * c + by_p[6] + 4 * by_q[6];
133 c = 8 * c + by_p[7] + 4 * by_q[7];
135 return c;
138 /* This one counts the number of vertices of each valence */
139 static uint64_t
140 dumb_checksum3(struct quiver const * const q)
142 uint64_t c = 0;
143 struct rational *r = 0;
144 struct rational *s = 0;
145 size_t by_v[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
147 for (size_t j = 0; j < q->v_num; ++j) {
148 size_t v = 0;
150 for (size_t k = 0; k < q->v_num; ++k) {
151 r = &q->e[j * q->v_len + k];
152 s = &q->e[k * q->v_len + j];
154 if (r->p ||
155 s->p) {
156 v++;
160 by_v[v % 8] += (1 << q->v[j].fatness);
163 c = by_v[0];
164 c = 8 * c + by_v[1];
165 c = 8 * c + by_v[2];
166 c = 8 * c + by_v[3];
167 c = 8 * c + by_v[4];
168 c = 8 * c + by_v[5];
169 c = 8 * c + by_v[6];
170 c = 8 * c + by_v[7];
172 return c;
175 /* This one counts the valence types (in/out/frozen/non-frozen) mod 4 */
176 static uint64_t
177 dumb_checksum4(struct quiver const * const q, size_t num_nonfrozen)
179 uint64_t c = 0;
180 struct rational *r = 0;
181 size_t by_i[4] = { 0, 0, 0, 0 };
182 size_t by_o[4] = { 0, 0, 0, 0 };
183 size_t by_n[4] = { 0, 0, 0, 0 };
184 size_t by_f[4] = { 0, 0, 0, 0 };
186 for (size_t j = 0; j < q->v_num; ++j) {
187 size_t v_i = 0;
188 size_t v_o = 0;
189 size_t v_n = 0;
190 size_t v_f = 0;
192 for (size_t k = 0; k < q->v_num; ++k) {
193 r = &q->e[j * q->v_len + k];
195 if (r->p > 0) {
196 v_o++;
197 } else if (r->p < 0) {
198 v_i++;
199 } else {
200 continue;
203 if (j >= num_nonfrozen &&
204 k >= num_nonfrozen) {
205 v_f++;
206 } else if (j < num_nonfrozen &&
207 k < num_nonfrozen) {
208 v_n++;
212 by_i[v_i % 4] += (1 << q->v[j].fatness);
213 by_o[v_o % 4] += (1 << q->v[j].fatness);
214 by_n[v_n % 4] += (1 << q->v[j].fatness);
215 by_f[v_f % 4] += (1 << q->v[j].fatness);
218 c = 0;
220 for (size_t j = 0; j < 4; ++j) {
221 c = (c << 8);
222 c = c + by_i[j] + 4 * (by_o[j] + 4 * (by_n[j] + 4 * by_f[j]));
225 return c;
228 /* This one counts number of floor(p*6/q) */
229 static uint64_t
230 dumb_checksum5(struct quiver const * const q)
232 uint64_t c = 0;
233 struct rational *r = 0;
234 struct rational *s = 0;
235 size_t by_e[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
237 for (size_t j = 0; j < q->v_num; ++j) {
238 for (size_t k = 0; k < q->v_num; ++k) {
239 r = &q->e[j * q->v_len + k];
240 s = &q->e[k * q->v_len + j];
241 by_e[(((((r->p * 6) / r->q) + 3) % 16) + 16) % 16]++;
242 by_e[(((((s->p * 6) / s->q) + 3) % 16) + 16) % 16]++;
246 c = 8 * 0 + by_e[0];
247 c = 8 * c + by_e[1];
248 c = 8 * c + by_e[2];
249 c = 8 * c + by_e[3];
250 c = 8 * c + by_e[4];
251 c = 8 * c + by_e[5];
252 c = 8 * c + by_e[6];
253 c = 8 * c + by_e[7];
255 return c;
258 /* This one computes rk(H_1(G)), so actually takes some effort */
259 static uint_fast8_t
260 dumb_checksum6(struct quiver const *q, int oriented)
262 uint_fast8_t r = 0;
263 uint_fast8_t *touched = 0;
264 size_t *frontier1 = 0;
265 size_t *frontier2 = 0;
266 size_t *temp = 0;
269 * This DFS implementation is absolute garbage, for the
270 * sake of simplicity over speed. It's not in a hot path,
271 * anyway.
273 if (!(touched = calloc(q->v_num, sizeof *touched))) {
274 goto done;
277 if (!(frontier1 = calloc(q->v_num + 1, sizeof *frontier1))) {
278 goto done;
281 if (!(frontier2 = calloc(q->v_num + 1, sizeof *frontier2))) {
282 goto done;
285 for (size_t k = 0; k <= q->v_num; ++k) {
286 frontier1[k] = (size_t) -1;
287 frontier2[k] = (size_t) -1;
290 for (size_t j = 0; j < q->v_num; ++j) {
291 int need_another_pass = 1;
293 if (touched[j]) {
294 continue;
297 touched[j] = 1;
298 frontier1[0] = j;
299 frontier1[1] = (size_t) -1;
301 while (need_another_pass) {
302 size_t fidx = 0;
304 need_another_pass = 0;
306 for (size_t k = 0; k < q->v_num; ++k) {
307 size_t kidx = frontier1[k];
309 if (kidx == (size_t) -1) {
310 break;
313 touched[kidx] = 1;
315 for (size_t l = 0; l < q->v_num; ++l) {
316 if (l == kidx) {
317 continue;
320 struct rational *e = &q->e[kidx *
321 q->v_len +
323 struct rational *f = &q->e[l *
324 q->v_len +
325 kidx];
327 if (e->p ||
328 (oriented &&
329 f->p)) {
330 if (touched[l]) {
331 r++;
332 } else {
333 frontier2[fidx] = l;
334 fidx++;
335 frontier2[fidx] =
336 (size_t) -1;
337 need_another_pass = 1;
338 touched[l] = 1;
344 temp = frontier1;
345 frontier1 = frontier2;
346 frontier2 = temp;
351 * Note that r is not actually the rank in the non-oriented
352 * case. We've double-counted every edge exactly once. No
353 * matter, we only care about using this as an invariant.
355 done:
356 free(touched);
357 free(frontier1);
358 free(frontier2);
360 return r;
363 /* Load start/end quivers, figure out frozen vertices, order things, etc. */
364 static int
365 setup(char *start_file, char *end_file, char **frozen, size_t num_frozen, struct
366 quiver *out_qstart, struct quiver *out_qend, size_t *out_num_nonfrozen)
368 struct quiver a = { 0 };
369 struct quiver b = { 0 };
370 FILE *fa = 0;
371 FILE *fb = 0;
372 const char *errstr = 0;
373 int ret = 0;
374 struct vertex **averts = 0;
375 struct vertex **bverts = 0;
376 size_t *a_to_start = 0;
377 size_t *b_to_end = 0;
379 if (!out_qstart ||
380 !out_qend) {
381 fprintf(stderr, L("invalid quivers"));
382 ret = EINVAL;
383 goto done;
386 if (out_qstart->v_num ||
387 out_qend->v_num) {
388 fprintf(stderr, L("output quivers are not empty\n"));
389 ret = EINVAL;
390 goto done;
393 if (!(fa = fopen(start_file, "r"))) {
394 ret = errno;
395 perror(L("fopen"));
396 fprintf(stderr, "cannot open file %s\n", start_file);
397 goto done;
400 if (!(fb = fopen(end_file, "r"))) {
401 ret = errno;
402 perror(L("fopen"));
403 fprintf(stderr, "cannot open file %s\n", end_file);
404 goto done;
407 if ((ret = quiver_load_from_file(&a, fa, &errstr))) {
408 fprintf(stderr, "%s\n", errstr);
409 goto done;
412 if ((ret = quiver_load_from_file(&b, fb, &errstr))) {
413 fprintf(stderr, "%s\n", errstr);
414 goto done;
417 if (a.v_num != b.v_num) {
418 fprintf(stderr, L(
419 "Quivers do not have same number of vertices\n"));
420 ret = EINVAL;
421 goto done;
425 * Make sure there are no duplicate vertices, but don't sort in
426 * place - that would change ordering, and ordering of vertices
427 * must match user input - tuning input so that known prefixes
428 * arise early is sometimes crucial.
430 if (!(averts = calloc(a.v_num, sizeof *averts))) {
431 ret = errno;
432 perror(L("calloc"));
433 goto done;
436 if (!(bverts = calloc(b.v_num, sizeof *bverts))) {
437 ret = errno;
438 perror(L("calloc"));
439 goto done;
442 for (size_t j = 0; j < a.v_num; ++j) {
443 averts[j] = &(a.v[j]);
444 bverts[j] = &(b.v[j]);
447 qsort(averts, a.v_num, sizeof(averts[0]), cmp);
448 qsort(bverts, b.v_num, sizeof(bverts[0]), cmp);
450 for (size_t j = 0; j < a.v_num; ++j) {
451 if (j &&
452 !strcmp(averts[j]->name, averts[j - 1]->name)) {
453 fprintf(stderr, L("Duplicate vertex \"%s\"\n"),
454 averts[j]->name);
455 ret = EINVAL;
456 goto done;
459 int d = strcmp(averts[j]->name, bverts[j]->name);
461 if (d > 0) {
462 fprintf(stderr, L("Vertex \"%s\" not in both files\n"),
463 bverts[j]->name);
464 ret = EINVAL;
465 goto done;
468 if (d < 0) {
469 fprintf(stderr, L("Vertex \"%s\" not in both files\n"),
470 averts[j]->name);
471 ret = EINVAL;
472 goto done;
475 if (averts[j]->fatness != bverts[j]->fatness) {
476 fprintf(stderr, L(
477 "Vertex \"%s\" has inconsistent fatness\n"),
478 averts[j]->name);
479 ret = EINVAL;
480 goto done;
484 /* a_to_s[i] = j <=> "vertex i in a is vertex j in out_qstart" */
485 if (!(a_to_start = calloc(a.v_num, sizeof(*a_to_start)))) {
486 ret = errno;
487 perror(L("calloc"));
488 goto done;
491 if (!(b_to_end = calloc(b.v_num, sizeof(*b_to_end)))) {
492 ret = errno;
493 perror(L("calloc"));
494 goto done;
498 * Order so that frozen vertices are all at the end - required
499 * for incrementation algorithm
501 for (uint_fast8_t want_frozen = 0; want_frozen <= 1; want_frozen++) {
502 for (size_t j = 0; j < a.v_num; ++j) {
503 uint_fast8_t is_frozen = 0;
505 for (size_t k = 0; k < num_frozen; ++k) {
506 is_frozen = is_frozen ||
507 !strcmp(a.v[j].name, frozen[k]);
509 if (is_frozen) {
510 k = num_frozen;
514 if (is_frozen != want_frozen) {
515 continue;
518 if ((ret = quiver_add_vertex(out_qstart,
519 &(a_to_start[j]),
520 a.v[j].name,
521 a.v[j].fatness, 0, 0, 0,
522 &errstr))) {
523 fprintf(stderr, "%s\n", errstr);
524 goto done;
527 if (!want_frozen &&
528 out_num_nonfrozen) {
529 *out_num_nonfrozen = a_to_start[j] + 1;
532 size_t tj = 0;
534 if ((ret = quiver_add_vertex(out_qend, &tj, a.v[j].name,
535 a.v[j].fatness, 0, 0, 0,
536 &errstr))) {
537 fprintf(stderr, "%s\n", errstr);
538 goto done;
541 for (size_t k = 0; k < b.v_num; ++k) {
542 if (!(strcmp(b.v[k].name,
543 out_qend->v[tj].name))) {
544 b_to_end[k] = tj;
550 /* Add the edges in correct order */
551 for (size_t j = 0; j < a.v_num; ++j) {
552 size_t tj = a_to_start[j];
554 for (size_t k = 0; k < a.v_num; ++k) {
555 size_t tk = a_to_start[k];
556 size_t idx_o = tj * out_qstart->v_len + tk;
557 size_t idx_a = j * a.v_len + k;
559 out_qstart->e[idx_o] = a.e[idx_a];
563 for (size_t j = 0; j < b.v_num; ++j) {
564 size_t tj = b_to_end[j];
566 for (size_t k = 0; k < b.v_num; ++k) {
567 size_t tk = b_to_end[k];
568 size_t idx_o = tj * out_qend->v_len + tk;
569 size_t idx_b = j * b.v_len + k;
571 out_qend->e[idx_o] = b.e[idx_b];
575 done:
576 quiver_clean(&a);
577 quiver_clean(&b);
579 if (fa) {
580 fclose(fa);
583 if (fb) {
584 fclose(fb);
587 free(averts);
588 free(bverts);
589 free(a_to_start);
590 free(b_to_end);
592 return ret;
595 /* Main loop */
597 main(int argc, char **argv)
599 int ret = 0;
600 struct quiver q = { 0 };
601 struct quiver qend = { 0 };
602 char *frozen[argc];
603 size_t fidx = 0;
604 char *start_file = 0;
605 char *end_file = 0;
606 long start_len_l = 0;
607 long max_len_l = 0;
608 size_t start_len = 0;
609 size_t max_len = (size_t) -1;
610 size_t warn_level = (size_t) -1;
611 struct rational *target = 0;
612 char *p = 0;
613 const char *errstr = 0;
614 uint_fast8_t verbose = 0;
615 uint_fast8_t require_all_nonfrozen = 0;
616 uint_fast8_t check_heuristics_only = 0;
617 int *seen_in_seq = 0;
618 uint_fast8_t desired_dumb_checksum1 = 0;
619 uint64_t desired_dumb_checksum2 = 0;
620 uint64_t desired_dumb_checksum3 = 0;
621 uint64_t desired_dumb_checksum4 = 0;
622 uint64_t desired_dumb_checksum5 = 0;
623 uint8_t desired_dumb_checksum6u = 0;
624 uint8_t desired_dumb_checksum6o = 0;
625 uint_fast8_t this_dumb_checksum1 = 0;
626 uint64_t this_dumb_checksum2 = 0;
627 uint64_t this_dumb_checksum3 = 0;
628 uint64_t this_dumb_checksum4 = 0;
629 uint64_t this_dumb_checksum5 = 0;
630 uint8_t this_dumb_checksum6u = 0;
631 uint8_t this_dumb_checksum6o = 0;
633 setlocale(LC_ALL, "");
634 int opt = 0;
636 for (int j = 0; j < argc; ++j) {
637 frozen[j] = 0;
640 while ((opt = getopt(argc, argv, "vhRHs:e:c:m:l:f:")) != -1) {
641 switch (opt) {
642 case 'v':
643 verbose++;
644 break;
645 case 'h':
646 usage(argv[0]);
647 ret = 0;
648 goto done;
649 case 'R':
650 require_all_nonfrozen = 1;
651 break;
652 case 'H':
653 check_heuristics_only = 1;
654 break;
655 case 's':
656 start_file = optarg;
657 break;
658 case 'e':
659 end_file = optarg;
660 break;
661 case 'c':
662 warn_level = strtoll(optarg, &p, 0);
664 if (p &&
665 *p) {
666 fprintf(stderr, "Invalid edge cap: %s\n",
667 optarg);
668 ret = EINVAL;
669 goto done;
672 break;
673 case 'l':
674 start_len_l = strtoll(optarg, &p, 0);
676 if ((start_len_l <= 0) ||
677 (p &&
678 *p)) {
679 fprintf(stderr, "Invalid start length: %s\n",
680 optarg);
681 ret = EINVAL;
682 goto done;
685 start_len = start_len_l;
686 break;
687 case 'm':
688 max_len_l = strtoll(optarg, &p, 0);
690 if ((max_len_l <= 0) ||
691 (p &&
692 *p)) {
693 fprintf(stderr, "Invalid max length: %s\n",
694 optarg);
695 ret = EINVAL;
696 goto done;
699 max_len = max_len_l;
700 break;
701 case 'f':
702 frozen[fidx] = optarg;
703 fidx++;
704 break;
705 default:
706 usage(argv[0]);
707 ret = EINVAL;
708 goto done;
712 if (!start_file ||
713 !end_file) {
714 usage(argv[0]);
715 ret = EINVAL;
716 goto done;
719 size_t num_nonfrozen = 0;
721 if (setup(start_file, end_file, frozen, fidx, &q, &qend,
722 &num_nonfrozen)) {
723 goto done;
726 if (warn_level == (size_t) -1) {
727 warn_level = 0;
729 for (size_t k = 0; k < q.v_num; ++k) {
730 if (warn_level < q.v[k].fatness) {
731 warn_level = q.v[k].fatness;
735 warn_level = warn_level + 1;
738 if (warn_level >= UINT_MAX) {
739 warn_level = UINT_MAX - 1;
742 if ((ret = quiver_set_warning_threshold(&q, warn_level, &errstr))) {
743 fprintf(stderr, "%s\n", errstr);
744 goto done;
747 /* Save off qend's vertices. XXX: Why not just USE qend? */
748 if ((qend.v_num * qend.v_num) / qend.v_num != qend.v_num) {
749 /* Should never happen - isn't qend.v_num capped? */
750 ret = errno = EOVERFLOW;
751 perror(L(""));
752 goto done;
755 if (!(target = calloc(qend.v_num * qend.v_num, sizeof *target))) {
756 ret = errno;
757 perror(L("calloc"));
758 goto done;
761 if (!(seen_in_seq = calloc(qend.v_num + 1, sizeof *seen_in_seq))) {
762 ret = errno;
763 perror(L("calloc"));
764 goto done;
767 for (size_t i = 0; i < qend.v_num * qend.v_num; ++i) {
768 target[i] = (struct rational) { .p = 0, .q = 1 };
771 for (size_t i = 0; i < qend.v_num; ++i) {
772 for (size_t j = 0; j < qend.v_num; ++j) {
773 target[i * qend.v_num + j] = qend.e[i * qend.v_len + j];
777 if (check_heuristics_only) {
778 desired_dumb_checksum1 = dumb_checksum1(&qend);
779 desired_dumb_checksum2 = dumb_checksum2(&qend);
780 desired_dumb_checksum3 = dumb_checksum3(&qend);
781 desired_dumb_checksum4 = dumb_checksum4(&qend, num_nonfrozen);
782 desired_dumb_checksum5 = dumb_checksum5(&qend);
783 desired_dumb_checksum6o = dumb_checksum6(&qend, 1);
784 desired_dumb_checksum6u = dumb_checksum6(&qend, 0);
787 /* Now, the long haul*/
788 size_t len = 0;
790 if (start_len) {
791 len = start_len - 1;
794 signal(SIGUSR1, handle);
795 size_t s = 0;
796 size_t c_idx;
797 int warn = 0;
798 uint_fast8_t correct = 0;
799 size_t *sequence = 0;
801 while (len < max_len) {
802 len++;
803 free(sequence);
804 sequence = calloc(len + 1, sizeof *sequence);
806 for (size_t k = 0; k < len; ++k) {
807 seen_in_seq[k] = 0;
810 for (size_t k = 0; k < len; ++k) {
811 /* This will get pruned, it's okay */
812 sequence[k] = 0;
813 seen_in_seq[sequence[k]]++;
816 c_idx = 0;
817 have_full_sequence:
819 if (status_requested) {
820 status_requested = 0;
821 printf("Current sequence %s", q.v[sequence[0]].name);
823 for (size_t k = 1; k < len; ++k) {
824 printf(", %s", q.v[sequence[k]].name);
827 printf("\n");
828 fflush(stdout);
832 * Here, we have computed a full sequence that we want
833 * to check. c_idx is in [0, len - 1], and the quiver
834 * has been mutated following the sequence up to (but
835 * not including) c_idx.
837 * This code is duplicated from increment_sequence
838 * because it is reasonable to start the whole process
839 * with a sequence that may, in fact, produce warnings.
841 warn = 0;
843 while (c_idx < len) {
844 /* Prune duplicates (mutations are involutions) */
845 if (c_idx &&
846 sequence[c_idx] == sequence[c_idx - 1]) {
847 goto increment_sequence;
850 /* Proceed one more mutation */
851 quiver_mutate(&q, sequence[c_idx], &warn, 0);
853 if (warn) {
854 /* That mutation caused a warning: skip it */
855 quiver_mutate(&q, sequence[c_idx], 0, 0);
857 /* And try the sequence, starting at c_idx */
858 goto increment_sequence;
862 * Skip ... X, Y ... when X > Y and the edge
863 * from X to Y is trivial
865 if (c_idx &&
866 sequence[c_idx] < sequence[c_idx - 1]) {
867 size_t x = sequence[c_idx - 1];
868 size_t y = sequence[c_idx];
869 struct rational *xy = &(q.e[x * q.v_len + y]);
870 struct rational *yx = &(q.e[y * q.v_len + x]);
872 if (!xy->p &&
873 !yx->p) {
874 /* Undo mutation */
875 quiver_mutate(&q, sequence[c_idx], 0,
878 /* And try the sequence, starting at c_idx */
879 goto increment_sequence;
883 c_idx++;
887 * If we are looking for a flip, we're probably
888 * going to require that all non-frozen vertices
889 * participate in the mutation.
891 if (require_all_nonfrozen) {
892 for (size_t k = 0; k < num_nonfrozen; ++k) {
893 if (!seen_in_seq[k]) {
894 goto finished_check;
900 * Here, there's a full sequence, and the quiver has
901 * been mutated according to it (all of it). We want
902 * to check if the result matches the target.
904 if (check_heuristics_only) {
905 /* Take a short-cut */
906 this_dumb_checksum1 = dumb_checksum1(&q);
908 if (this_dumb_checksum1 != desired_dumb_checksum1) {
909 goto finished_check;
912 this_dumb_checksum2 = dumb_checksum2(&q);
914 if (this_dumb_checksum2 != desired_dumb_checksum2) {
915 goto finished_check;
918 this_dumb_checksum3 = dumb_checksum3(&q);
920 if (this_dumb_checksum3 != desired_dumb_checksum3) {
921 goto finished_check;
924 this_dumb_checksum4 = dumb_checksum4(&q, num_nonfrozen);
926 if (this_dumb_checksum4 != desired_dumb_checksum4) {
927 goto finished_check;
930 this_dumb_checksum5 = dumb_checksum5(&q);
932 if (this_dumb_checksum5 != desired_dumb_checksum5) {
933 goto finished_check;
936 this_dumb_checksum6o = dumb_checksum6(&q, 1);
938 if (this_dumb_checksum6o != desired_dumb_checksum6o) {
939 goto finished_check;
942 this_dumb_checksum6u = dumb_checksum6(&q, 0);
944 if (this_dumb_checksum6u != desired_dumb_checksum6u) {
945 goto finished_check;
948 goto print_this;
951 correct = 1;
953 for (size_t i = 0; i < q.v_num; ++i) {
954 for (size_t j = 0; j < q.v_num; ++j) {
955 struct rational *e = &(q.e[i * q.v_len + j]);
956 struct rational *f = &(target[i * q.v_num + j]);
958 if (e->p * f->q != f->p * e->q) {
959 correct = 0;
960 i = q.v_num + 1;
961 j = i;
966 print_this:
968 if (correct ||
969 check_heuristics_only) {
970 printf("SEQUENCE: %s", q.v[sequence[0]].name);
972 for (size_t k = 1; k < len; ++k) {
973 printf(", %s", q.v[sequence[k]].name);
976 printf("\n");
977 fflush(stdout);
980 finished_check:
981 c_idx = len - 1;
982 quiver_mutate(&q, sequence[c_idx], 0, 0);
983 increment_sequence:
986 * Here, we have some kind of sequence. The quiver agrees
987 * with it up to (but not including) c_idx, and we want
988 * to increment it at c_idx.
990 s++;
991 seen_in_seq[sequence[c_idx]]--;
992 sequence[c_idx]++;
993 seen_in_seq[sequence[c_idx]]++;
995 if (verbose &&
996 c_idx == 2) {
997 double completed = sequence[0] * num_nonfrozen *
998 num_nonfrozen + sequence[1] *
999 num_nonfrozen +
1000 sequence[2] - 1;
1001 double total = num_nonfrozen * num_nonfrozen *
1002 num_nonfrozen;
1004 printf("Length %zu: completed %.3g%%\n", len, (100.0 *
1005 completed)
1006 / total);
1007 fflush(stdout);
1010 /* Skip over repeats: each mutation is an involution */
1011 if (c_idx > 0 &&
1012 sequence[c_idx] == sequence[c_idx - 1]) {
1013 seen_in_seq[sequence[c_idx]]--;
1014 sequence[c_idx]++;
1015 seen_in_seq[sequence[c_idx]]++;
1018 /* Wrap back to 0 */
1019 if (sequence[c_idx] >= num_nonfrozen) {
1020 if (!c_idx) {
1021 goto exhausted_len;
1024 c_idx--;
1025 quiver_mutate(&q, sequence[c_idx], 0, 0);
1026 goto increment_sequence;
1029 if (c_idx != len - 1) {
1031 * Here, we now have a start of a sequence.
1032 * The quiver agrees with our start up to (but
1033 * not including) c_idx, and we want to possibly
1034 * complete this to a full sequence. First,
1035 * however, we need to make sure that we don't
1036 * introduce any warnings at c_idx.
1038 * This single-mutation testing block could be
1039 * ignored, but I have a vague suspicion it will
1040 * be a hot path.
1042 warn = 0;
1043 quiver_mutate(&q, sequence[c_idx], &warn, 0);
1045 if (warn) {
1046 /* That mutation caused a warning: skip it */
1047 quiver_mutate(&q, sequence[c_idx], 0, 0);
1049 /* And try the sequence, starting at c_idx */
1050 goto increment_sequence;
1054 * Quivers almost commute: in particular if
1055 * the edge v_i -> v_j is zero, then mutation
1056 * at v_i and v_j commute. So if we're at X, Y
1057 * in the list, and X > Y, then we've actually
1058 * already computed that branch of the tree,
1059 * back when it was Y, X. Therefore, increment X.
1061 if (c_idx &&
1062 sequence[c_idx] < sequence[c_idx - 1]) {
1063 size_t x = sequence[c_idx - 1];
1064 size_t y = sequence[c_idx];
1065 struct rational *xy = &(q.e[x * q.v_len + y]);
1066 struct rational *yx = &(q.e[y * q.v_len + x]);
1068 if (!xy->p &&
1069 !yx->p) {
1070 /* Undo mutation */
1071 quiver_mutate(&q, sequence[c_idx], 0,
1074 /* And try the sequence, starting at c_idx */
1075 goto increment_sequence;
1080 * Here the quiver agrees with our start up
1081 * to and including c_idx, and we need to fill
1082 * it out into a full sequence so that it can
1083 * be tested. We don't need to perform all the
1084 * mutations of that full sequence, but we do
1085 * need to generate the minimal one.
1087 for (size_t i = c_idx + 1; i < len; ++i) {
1088 seen_in_seq[sequence[i]]--;
1089 sequence[i] = 1 - !!((i - c_idx) % 2);
1090 seen_in_seq[sequence[i]]++;
1093 c_idx++;
1096 goto have_full_sequence;
1097 exhausted_len:
1100 * Here, we have c_idx = 0, and the quiver has not been
1101 * mutated by anything in sequence
1103 printf("Exhausted length %zu\n", len);
1106 free(sequence);
1107 fflush(stdout);
1108 done:
1109 free(target);
1110 free(seen_in_seq);
1112 return ret;