26,27c26,35
< // usage: fsearch TABLE -k K [-root t | -cross | -flag y] [-thr t] [-ori 0|1] [-ell 0|1] [-nofresh] [-nosame]
< //                [-quick 0|1] [-fast 0|1] [-sub S I] [-maxnodes n] [-maxdepth d] [-sol FILE] [-id NAME]
---
> // usage: fsearch TABLE -k K [-root t | -cross | -flag y | -fixfile F] [-thr t] [-ori 0|1] [-ell 0|1] [-nofresh] [-nosame]
> //                [-quick 0|1] [-quick2 0|1] [-fast 0|1] [-keys M] [-small S] [-resel d] [-heur 0|1] [-adapt T]
> //                [-sub S I] [-maxnodes n] [-maxdepth d] [-dump FILE] [-sol FILE] [-id NAME]
> //   -keys M    number of facets whose candidate lists are kept (production runs: 8)
> //   -adapt T   if the shortest list has more than T entries, also compute the list of the unglued facet with the most
> //              glued neighbours and branch on it if it is shorter (development option; default off)
> //   -fixfile F impose the gluings listed in F ("x y" = rho(x) = y in H-coordinates) instead of a root
> //   -maxdepth d -dump FILE   stop at depth d and write the partial gluings of the cut nodes
> // The production runs of enumeration (B) used the binary built from fsearch_production.c, which is this file without
> // the options -fixfile, -heur, -adapt, -quick2 (all off by default here); node counts are identical.
197c205
< static int MKEYS = 4, SSMALL = 8, RESEL = 0;
---
> static int MKEYS = 4, SSMALL = 8, RESEL = 0, HEUR = 0, ADAPT = 0;
207a216,217
> static int QUICK2;
> static inline int quick2_ok(int best, int Fp, int k);
257a268
>                 if (QUICK2 && QUICK && MLAB == 3 && !fresh && !quick2_ok(best, Fp, k)) continue;
288a300,327
> // second quick necessary test (m = 3), from the side of the candidate partner facet Fp: for every ridge of Fp whose
> // neighbour N' (same cell as Fp) is glued, the candidate rho(best<<8) = (Fp<<8)|k forces rho(x4) = x3 with
> // x3 = rho(x0 r_n) r_n (through N') and x4 = rho(x0) r_n, where x0 is the ridge flag of Fp and rho(x0) lies in `best`.
> // Same case distinction as quick_ok.  Only a filter: everything it rejects would also be rejected by glue/prop.
> static int QUICK2 = 0;
> static inline int quick2_ok(int best, int Fp, int k) {
>     int c = cellF[Fp], fp = Fp - c * NF; int cg = cellF[best];
>     const int *ad = &adj[fp * ND]; int kk = kinv[k] * NK;
>     for (int j = 0; j < ND; j++) {
>         if (tgt[c * NF + ad[j]] < 0) continue;
>         int x0 = (Fp << 8) | dreps[j];
>         int x3 = Rn(rho(Rn(x0))); int F3 = x3 >> 8;
>         int x4 = Rn((best << 8) | Kmul[kk + dreps[j]]); int F4 = x4 >> 8;
>         if (tgt[F4] >= 0) { if (!(tgt[F3] >= 0 && rho(x4) == x3)) return 0; continue; }
>         if (tgt[F3] >= 0) return 0;
>         if (F4 == F3) return 0;
>         if (F3 == best) { if (F4 != Fp) return 0; continue; }
>         if (F3 == Fp) { if (F4 != best) return 0; continue; }
>         if (F4 == best || F4 == Fp) return 0;
>         int c3 = cellF[F3];
>         if (c3 == cg) {
>             if (NOSAME) return 0;
>             int t = Kmul[(x3 & 255) * NK + kinv[x4 & 255]];
>             if (!okc[gclsFP[((size_t)(F4 - cg * NF) * NF + (F3 - c3 * NF)) * NK + t]]) return 0;
>         }
>     }
>     return 1;
> }
303a343
>         if (QUICK2 && QUICK && MLAB == 3 && !quick2_ok(best, y >> 8, y & 255)) continue;
359c399,409
<     int b = 0; for (int i = 1; i < n; i++) if (loc[i].size < loc[b].size) b = i;
---
>     int b = 0;
>     if (HEUR == 0) { for (int i = 1; i < n; i++) if (loc[i].size < loc[b].size) b = i; }
>     else {                                                     // first refresh the key with the most glued neighbours
>         int bs = -1;
>         for (int i = 0; i < n; i++) {
>             int F = loc[i].F, c = cellF[F], f = F - c * NF, cnt = 0;
>             for (int j = 0; j < ND; j++) if (tgt[c * NF + adj[f * ND + j]] >= 0) cnt++;
>             int sc = cnt * 100000 - (loc[i].size < 99999 ? loc[i].size : 99999);
>             if (sc > bs) { bs = sc; b = i; }
>         }
>     }
360a411
>     if (HEUR == 1) { for (int i = 0; i < n; i++) if (loc[i].fresh && loc[i].size < loc[b].size) b = i; }
364a416,436
>     if (ADAPT && loc[b].size > ADAPT && n < MAXKEYS) {
>         // the shortest list is still long: add the unglued non-key facet with the most glued neighbours as a new key
>         // (exact list from scratch) and branch on it if its list is shorter.  Changes only the branching order.
>         int best = -1, bestc = -1;
>         for (int c = 0; c < KC; c++) {
>             if (ndef[c] == 0) continue;
>             for (int f = 0; f < NF; f++) {
>                 int F = c * NF + f; if (tgt[F] >= 0) continue;
>                 int isk = 0; for (int i = 0; i < n; i++) if (loc[i].F == F) isk = 1;
>                 if (isk) continue;
>                 int cnt = 0; for (int j = 0; j < ND; j++) if (tgt[c * NF + adj[f * ND + j]] >= 0) cnt++;
>                 if (cnt > bestc) { bestc = cnt; best = F; }
>             }
>         }
>         if (best >= 0) {
>             loc[n].F = best; scratch_domain(&loc[n]);
>             if (loc[n].size == 0) { atop = save; return; }
>             if (loc[n].size < loc[b].size) b = n;
>             n++;
>         }
>     }
380c452
<     int mode = -1, rootarg = 0, thrset = 0; const char *solname = NULL;
---
>     int mode = -1, rootarg = 0, thrset = 0; const char *solname = NULL, *fixname = NULL;
385a458
>         else if (!strcmp(argv[i], "-fixfile")) { mode = 3; fixname = argv[++i]; }
397a471,473
>         else if (!strcmp(argv[i], "-heur")) HEUR = atoi(argv[++i]);
>         else if (!strcmp(argv[i], "-adapt")) ADAPT = atoi(argv[++i]);
>         else if (!strcmp(argv[i], "-quick2")) QUICK2 = atoi(argv[++i]);
444a521,535
>     if (mode == 3) {
>         // -fixfile: lines "x y" (flags in H-coordinates c*|H|+h): impose rho(x) = y for all lines, then search
>         FILE *ff = fopen(fixname, "r"); if (!ff) { fprintf(stderr, "cannot open fix file\n"); return 2; }
>         int xa, ya, ok = 1;
>         while (ok && fscanf(ff, "%d %d", &xa, &ya) == 2) {
>             if (xa < 0 || xa >= KC * N || ya < 0 || ya >= KC * N) { fprintf(stderr, "bad flag in fix file\n"); return 1; }
>             int xc = xa / N, xh = xa - xc * N, yc2 = ya / N, yh2 = ya - yc2 * N;
>             int xe = ((xc * NF + facet[xh]) << 8) | pos[xh], ye = ((yc2 * NF + facet[yh2]) << 8) | pos[yh2];
>             int r = glue(xe, ye);
>             if (r == 0) ok = 0;
>             else if (r == 1) { queue[0] = xe >> 8; queue[1] = ye >> 8; if (!prop(2)) ok = 0; }
>         }
>         fclose(ff);
>         if (ok) rec(1, NULL, 0);
>     } else {
453a545
>     }
