// Hyperbolic Rogue -- Field Quotient geometry // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details /** \file fieldpattern.cpp * \brief Field Quotient geometry */ #include "hyper.h" #if CAP_FIELD namespace hr { EX namespace fieldpattern { EX bool use_rule_fp = false; EX bool use_quotient_fp = false; int limitsq = 10; int limitp = 10000; int limitv = 100000; #if HDR #define currfp fieldpattern::getcurrfp() struct primeinfo { int p; int cells; bool squared; }; struct fgeomextra { eGeometry base; vector<primeinfo> primes; vector<int> dualval; int current_prime_id; fgeomextra(eGeometry b, int i) : base(b), current_prime_id(i) {} }; #endif EX bool isprime(int n) { for(int k=2; k<n; k++) if(n%k == 0) return false; return true; } #if HDR #define MWDIM (mproduct ? 3 : WDIM+1) struct matrix : array<array<int, MAXMDIM>, MAXMDIM> { bool operator == (const matrix& B) const { for(int i=0; i<MWDIM; i++) for(int j=0; j<MWDIM; j++) if(self[i][j] != B[i][j]) return false; return true; } bool operator != (const matrix& B) const { for(int i=0; i<MWDIM; i++) for(int j=0; j<MWDIM; j++) if(self[i][j] != B[i][j]) return true; return false; } bool operator < (const matrix& B) const { for(int i=0; i<MWDIM; i++) for(int j=0; j<MWDIM; j++) if(self[i][j] != B[i][j]) return self[i][j] < B[i][j]; return false; } }; #endif EX int groupspin(int id, int d, int group) { return group*(id/group) + (id + d) % group; } EX int btspin(int id, int d) { return groupspin(id, d, S7); } #if HDR static constexpr int ERR = -99; struct triplet_info { int i, j, size; }; struct fpattern { unsigned force_hash; int Prime, wsquare, Field, dual; // we perform our computations in the field Z_Prime[w] where w^2 equals wsquare // (or simply Z_Prime for wsquare == 0) #define EASY // 'easy' assumes that all elements of the field actually used // are of form n or mw (not n+mw), and cs and ch are both of form n // by experimentation, such cs and ch always exist // many computations are much simpler under that assumption #ifndef EASY static int neasy; int m(int x) { x %= Prime; if(x<0) x+= Prime; return x; } #endif int sub(int a, int b) { #ifdef EASY return (a + b * (Prime-1)) % Prime; #else return m(a%Prime-b%Prime) + Prime * m(a/Prime-b/Prime); #endif } int add(int a, int b) { #ifdef EASY if(a == ERR || b == ERR || a*b<0) return ERR; return (a+b)%Prime; #else return m(a%Prime+b%Prime) + Prime * m(a/Prime+b/Prime); #endif } int mul(int tx, int ty) { #ifdef EASY return (tx*ty*((tx<0&&ty<0)?wsquare:1)) % Prime; #else if(tx >= Prime && tx % Prime) neasy++; if(ty >= Prime && ty % Prime) neasy++; int x[2], y[2], z[3]; for(int i=0; i<3; i++) z[i] = 0; for(int i=0; i<2; i++) x[i] = tx%Prime, tx /= Prime; for(int i=0; i<2; i++) y[i] = ty%Prime, ty /= Prime; for(int i=0; i<2; i++) for(int j=0; j<2; j++) z[i+j] = (z[i+j] + x[i] * y[j]) % Prime; z[0] += z[2] * wsquare; return m(z[0]) + Prime * m(z[1]); #endif } int sqr(int x) { return mul(x,x); } int err; matrix mmul(const matrix& A, const matrix& B) { matrix res; for(int i=0; i<MWDIM; i++) for(int k=0; k<MWDIM; k++) { int t = 0; #ifdef EASY int tp = 0, tn = 0; for(int j=0; j<MWDIM; j++) { int val = mul(A[i][j], B[j][k]); if(val > 0) tp += val; else tn += val; } tp %= Prime; tn %= Prime; if(tp && tn) err++; t = tp + tn; #else for(int j=0; j<MWDIM; j++) t = add(t, mul(A[i][j], B[j][k])); #endif res[i][k] = t; } return res; } map<matrix, int> matcode; vector<matrix> matrices; vector<string> qpaths; vector<matrix> qcoords; // S7 in 2D, but e.g. 4 for a 3D cube int rotations; // S7 in 2D, but e.g. 24 for a 3D cube int local_group; // Id: Identity // R : rotate by 1/rotations of the full circle // P : make a step and turn backwards // X : in 3-dim, turn by 90 degrees matrix Id, R, P, X; matrix strtomatrix(string s) { matrix res = Id; matrix m = Id; for(int i=isize(s)-1; i>=0; i--) if(s[i] == 'R') res = mmul(R, res); else if (s[i] == 'P') res = mmul(P, res); else if (s[i] == 'x') { m[0][0] = -1; res = mmul(m, res); m[0][0] = +1; } else if (s[i] == 'y') { m[1][1] = -1; res = mmul(m, res); m[1][1] = +1; } else if (s[i] == 'z') { m[2][2] = -1; res = mmul(m, res); m[2][2] = +1; } return res; } void addas(const matrix& M, int i) { if(!matcode.count(M)) { matcode[M] = i; for(int j=0; j<isize(qcoords); j++) addas(mmul(M, qcoords[j]), i); } } void add(const matrix& M) { if(!matcode.count(M)) { int i = isize(matrices); matcode[M] = i, matrices.push_back(M); for(int j=0; j<isize(qcoords); j++) addas(mmul(M, qcoords[j]), i); if(WDIM == 3) add(mmul(X, M)); add(mmul(R, M)); } } #define MXF 1000000 vector<int> connections; vector<int> inverses; // NYI in 3D // 2D only vector<int> rrf; // rrf[i] equals gmul(i, rotations-1) vector<int> rpf; // rpf[i] equals gmul(i, rotations) matrix mpow(matrix M, int N) { while((N&1) == 0) N >>= 1, M = mmul(M, M); matrix res = M; N >>= 1; while(N) { M = mmul(M,M); if(N&1) res = mmul(res, M); N >>= 1; } return res; } int gmul(int a, int b) { return matcode[mmul(matrices[a], matrices[b])]; } int gpow(int a, int N) { return matcode[mpow(matrices[a], N)]; } int gorder(int a) { int b = a; int q = 1; while(b) b = gmul(b, a), q++; return q; } pair<int,bool> gmul(pair<int, bool> a, int b) { return make_pair(gmul(a.first,b), a.second); } int order(const matrix& M); string decodepath(int i) { string s; while(i) { if(i % S7) i--, s += 'R'; else i = connections[i], s += 'P'; } return s; } int orderstats(); int cs, sn, ch, sh; int solve(); void build(); static constexpr int MAXDIST = 120; vector<char> disthep; vector<char> disthex; vector<char> distwall, distriver, distwall2, distriverleft, distriverright, distflower; int distflower0; vector<eItem> markers; int getdist(pair<int,bool> a, vector<char>& dists); int getdist(pair<int,bool> a, pair<int,bool> b); int dijkstra(vector<char>& dists, vector<int> indist[MAXDIST]); void analyze(); int maxdist, otherpole, circrad, wallid, wallorder, riverid; bool easy(int i) { return i < Prime || !(i % Prime); } // 11 * 25 // (1+z+z^3) * (1+z^3+z^4) == // 1+z+z^7 == 1+z+z^2(z^5) == 1+z+z^2(1+z^2) = 1+z+z^2+z^4 void init(int p) { Prime = p; if(solve()) { printf("error: could not solve the fieldpattern\n"); exit(1); } build(); analyze(); } fpattern(int p) { force_hash = 0; #if CAP_THREAD && MAXMDIM >= 4 dis = nullptr; #endif if(!p) return; init(p); } void findsubpath(); vector<matrix> generate_isometries(); bool check_order(matrix M, int req); unsigned compute_hash(); void set_field(int p, int sq); unsigned hashv; #if MAXMDIM >= 4 // general 4D vector<transmatrix> fullv; void add1(const matrix& M); void add1(const matrix& M, const transmatrix& Full); vector<matrix> generate_isometries3(); int solve3(); bool generate_all3(); #if CAP_THREAD struct discovery *dis; #endif #endif vector<triplet_info> find_triplets(); void generate_quotientgroup(); }; #if CAP_THREAD && MAXMDIM >= 4 struct discovery { fpattern experiment; std::shared_ptr<std::thread> discoverer; std::mutex lock; std::condition_variable cv; bool is_suspended; bool stop_it; map<unsigned, tuple<int, int, matrix, matrix, matrix, int> > hashes_found; discovery() : experiment(0) { is_suspended = false; stop_it = false; experiment.dis = this; experiment.Prime = experiment.Field = experiment.wsquare = 0; } void activate(); void suspend(); void check_suspend(); void schedule_destruction(); void discovered(); ~discovery(); }; #endif #endif bool fpattern::check_order(matrix M, int req) { int err = 0; matrix P = M; for(int i=1; i<req; i++) { if(P == Id) return false; P = mmul(P, M); } return P == Id && !err; } vector<matrix> fpattern::generate_isometries() { matrix T = Id; int low = wsquare ? 1-Prime : 0; vector<matrix> res; auto colprod = [&] (int a, int b) { return add(add(mul(T[0][a], T[0][b]), mul(T[1][a], T[1][b])), mul(T[2][a], T[2][b])); }; for(T[0][0]=low; T[0][0]<Prime; T[0][0]++) for(T[1][0]=low; T[1][0]<Prime; T[1][0]++) for(T[2][0]=low; T[2][0]<Prime; T[2][0]++) if(colprod(0, 0) == 1) for(T[0][1]=low; T[0][1]<Prime; T[0][1]++) for(T[1][1]=low; T[1][1]<Prime; T[1][1]++) for(T[2][1]=low; T[2][1]<Prime; T[2][1]++) if(colprod(1, 1) == 1) if(colprod(1, 0) == 0) for(T[0][2]=low; T[0][2]<Prime; T[0][2]++) for(T[1][2]=low; T[1][2]<Prime; T[1][2]++) for(T[2][2]=low; T[2][2]<Prime; T[2][2]++) if(colprod(2, 2) == 1) if(colprod(2, 0) == 0) if(colprod(2, 1) == 0) res.push_back(T); return res; } #if MAXMDIM >= 4 vector<matrix> fpattern::generate_isometries3() { matrix T = Id; int low = wsquare ? 1-Prime : 0; vector<matrix> res; auto colprod = [&] (int a, int b) { return add(add(mul(T[0][a], T[0][b]), mul(T[1][a], T[1][b])), sub(mul(T[2][a], T[2][b]), mul(T[3][a], T[3][b]))); }; auto rowcol = [&] (int a, int b) { return add(add(mul(T[a][0], T[0][b]), mul(T[a][1], T[1][b])), add(mul(T[a][2], T[2][b]), mul(T[a][3], T[3][b]))); }; for(T[0][0]=low; T[0][0]<Prime; T[0][0]++) for(T[1][0]=low; T[1][0]<Prime; T[1][0]++) for(T[2][0]=low; T[2][0]<Prime; T[2][0]++) for(T[3][0]=low; T[3][0]<Prime; T[3][0]++) if(colprod(0, 0) == 1) for(T[0][1]=low; T[0][1]<Prime; T[0][1]++) for(T[1][1]=low; T[1][1]<Prime; T[1][1]++) for(T[2][1]=low; T[2][1]<Prime; T[2][1]++) for(T[3][1]=low; T[3][1]<Prime; T[3][1]++) if(colprod(1, 1) == 1) if(colprod(1, 0) == 0) { #if CAP_THREAD && MAXMDIM >= 4 if(dis) dis->check_suspend(); if(dis && dis->stop_it) return res; #endif for(T[0][2]=low; T[0][2]<Prime; T[0][2]++) for(T[0][3]=low; T[0][3]<Prime; T[0][3]++) if(rowcol(0, 0) == 1) if(rowcol(0, 1) == 0) for(T[1][2]=low; T[1][2]<Prime; T[1][2]++) for(T[1][3]=low; T[1][3]<Prime; T[1][3]++) if(rowcol(1, 0) == 0) if(rowcol(1, 1) == 1) for(T[2][2]=low; T[2][2]<Prime; T[2][2]++) for(T[3][2]=low; T[3][2]<Prime; T[3][2]++) if(colprod(2, 2) == 1) if(colprod(2, 0) == 0) if(colprod(2, 1) == 0) for(T[2][3]=low; T[2][3]<Prime; T[2][3]++) for(T[3][3]=low; T[3][3]<Prime; T[3][3]++) if(rowcol(2, 0) == 0) if(rowcol(2, 1) == 0) if(rowcol(2, 2) == 1) // if(colprod(3, 3) == 1) if(add(colprod(3, 3), 1) == 0) if(colprod(3, 0) == 0) if(colprod(3, 1) == 0) if(colprod(3, 2) == 0) if(rowcol(3, 3) == 1) if(rowcol(3, 0) == 0) if(rowcol(3, 1) == 0) if(rowcol(3, 2) == 0) res.push_back(T); if(isize(res) > limitp) return res; } return res; } void fpattern::add1(const matrix& M) { if(!matcode.count(M)) { int i = isize(matrices); matcode[M] = i, matrices.push_back(M); } } void fpattern::add1(const matrix& M, const transmatrix& Full) { if(!matcode.count(M)) { int i = isize(matrices); matcode[M] = i, matrices.push_back(M), fullv.push_back(Full); } } #endif map<unsigned,int> hash_found; unsigned fpattern::compute_hash() { unsigned hashv = 0; int iR = matcode[R]; int iP = matcode[P]; int iX = matcode[X]; for(int i=0; i<isize(matrices); i++) { hashv = 3 * hashv + gmul(i, iP) + 7 * gmul(i, iR); if(MWDIM == 4) hashv += 11 * gmul(i, iX); } return hashv; } #if MAXMDIM >= 4 bool fpattern::generate_all3() { reg3::generate_fulls(); err = 0; matrices.clear(); matcode.clear(); add1(Id); fullv = {hr::Id}; for(int i=0; i<isize(matrices); i++) { add1(mmul(matrices[i], R), fullv[i] * cgi.full_R); add1(mmul(matrices[i], X), fullv[i] * cgi.full_X); if(err) return false; } local_group = isize(matrices); if(local_group != isize(cgi.cellrotations)) return false; for(int i=0; i<(int)matrices.size(); i++) { matrix E = mmul(matrices[i], P); if(!matcode.count(E)) for(int j=0; j<local_group; j++) add1(mmul(E, matrices[j])); if(err) return false; if(isize(matrices) >= limitv) { println(hlog, "limitv exceeded"); return false; } } hashv = compute_hash(); DEBB(DF_FIELD, ("all = ", isize(matrices), "/", local_group, " = ", isize(matrices) / local_group, " hash = ", hashv, " count = ", ++hash_found[hashv])); if(use_quotient_fp) generate_quotientgroup(); return true; } void fpattern::generate_quotientgroup() { int MS = isize(matrices); int best_p = 0, best_i = 0; for(int i=0; i<MS; i++) { int j = i, p = 1; while(j >= local_group) j = gmul(j, i), p++; if(j == 0 && p > best_p) { bool okay = true; vector<bool> visited(MS, false); for(int ii=0; ii<MS; ii++) if(!visited[ii]) { int jj = ii; for(int k=0; k<p; k++) { if(k && jj/local_group == ii/local_group) okay = false; visited[jj] = true; jj = gmul(i, jj); } } if(okay) { bool chk = (MS/p) % local_group; println(hlog, "quotient by ", i, " : ", p, " times less, ", (MS/p/local_group), " tiles, check ", chk); best_p = p; best_i = i; if(chk) { exit(1); } } } } if(best_p > 1) { vector<int> new_id(MS, -1); vector<int> orig_id(MS, -1); vector<matrix> new_matrices; int nv = 0; for(int i=0; i<MS; i++) if(new_id[i] == -1) { int prode = i; for(int l=0; l<local_group; l++) { new_matrices.push_back(matrices[i+l]); } for(int k=0; k<best_p; k++) { for(int l=0; l<local_group; l++) { new_id[gmul(prode, l)] = nv + l; } prode = gmul(best_i, prode); } nv += local_group; } println(hlog, "got nv = ", nv, " / ", local_group); for(int i=0; i<MS; i++) matcode[matrices[i]] = new_id[i]; matrices = std::move(new_matrices); println(hlog, "size matrices = ", isize(matrices), " size matcode = ", isize(matcode)); println(hlog, tie(P, R, X)); /*println(hlog, "TRY AGAIN"); generate_quotientgroup(); exit(1);*/ } } EX purehookset hooks_solve3; int fpattern::solve3() { reg3::generate_fulls(); DEBB(DF_FIELD, ("generating isometries for ", Field)); auto iso3 = generate_isometries(); auto iso4 = generate_isometries3(); int cmb = 0; vector<matrix> possible_P, possible_X, possible_R; for(auto& M: iso3) { if(check_order(M, 2)) possible_X.push_back(M); if(check_order(M, cgi.r_order)) possible_R.push_back(M); } for(auto& M: iso4) if(check_order(M, 2)) possible_P.push_back(M); DEBB(DF_FIELD, ("field = ", Field, " #P = ", isize(possible_P), " #X = ", isize(possible_X), " #R = ", isize(possible_R), " r_order = ", cgi.r_order, " xp_order = ", cgi.xp_order)); for(auto& xX: possible_X) for(auto& xP: possible_P) if(check_order(mmul(xP, xX), cgi.xp_order)) for(auto& xR: possible_R) if(check_order(mmul(xR, xX), cgi.rx_order)) { err = 0; if(mmul(xX, xP) != mmul(xR, mmul(mmul(xP, xX), xR))) continue; if(err) continue; #if CAP_THREAD && MAXMDIM >= 4 if(dis) dis->check_suspend(); if(dis && dis->stop_it) return 0; #endif P = xP; R = xR; X = xX; if(!generate_all3()) continue; callhooks(hooks_solve3); #if CAP_THREAD && MAXMDIM >= 4 if(dis) { dis->discovered(); continue; } #endif if(force_hash && hashv != force_hash) continue; cmb++; goto ok; } ok: DEBB(DF_FIELD, ("cmb = ", cmb, " for field = ", Field)); return cmb; } #endif void fpattern::set_field(int p, int sq) { Prime = p; Field = sq ? Prime*Prime : Prime; wsquare = sq; for(int a=0; a<MWDIM; a++) for(int b=0; b<MWDIM; b++) Id[a][b] = a==b?1:0; } int fpattern::solve() { for(int a=0; a<MWDIM; a++) for(int b=0; b<MWDIM; b++) Id[a][b] = a==b?1:0; if(!isprime(Prime)) { return 1; } rotations = WDIM == 2 ? S7 : 4; local_group = WDIM == 2 ? S7 : 24; for(dual=0; dual<3; dual++) { for(int pw=1; pw<3; pw++) { if(pw>3) break; Field = pw==1? Prime : Prime*Prime; if(pw == 2) { for(wsquare=1; wsquare<Prime; wsquare++) { int roots = 0; for(int a=0; a<Prime; a++) if((a*a)%Prime == wsquare) roots++; if(!roots) break; } } else wsquare = 0; #if MAXMDIM >= 4 if(WDIM == 3) { if(dual == 0 && (Prime <= limitsq || pw == 1)) { int s = solve3(); if(s) return 0; } continue; } #endif if(dual == 2) { if(Field <= 10) { vector<matrix> all_isometries = generate_isometries(); for(auto& X: all_isometries) if(check_order(X, rotations)) for(auto& Y: all_isometries) if(check_order(Y, 2) && check_order(mmul(X, Y), S3)) { R = X; P = Y; return 0; } } continue; } #ifdef EASY std::vector<int> sqrts(Prime, 0); for(int k=1-Prime; k<Prime; k++) sqrts[sqr(k)] = k; int fmax = Prime; #else std::vector<int> sqrts(Field); for(int k=0; k<Field; k++) sqrts[sqr(k)] = k; int fmax = Field; #endif R = P = X = Id; X[1][1] = 0; X[2][2] = 0; X[1][2] = 1; X[2][1] = Prime-1; for(cs=0; cs<fmax; cs++) { int sb = sub(1, sqr(cs)); sn = sqrts[sb]; R[0][0] = cs; R[1][1] = cs; R[0][1] = sn; R[1][0] = sub(0, sn); if(!check_order(R, dual ? S3 : rotations)) continue; if(R[0][0] == 1) continue; for(ch=2; ch<fmax; ch++) { int chx = sub(mul(ch,ch), 1); sh = sqrts[chx]; P[0][0] = sub(0, ch); P[0][WDIM] = sub(0, sh); P[1][1] = Prime-1; P[WDIM][0] = sh; P[WDIM][WDIM] = ch; if(!check_order(mmul(P, R), dual ? rotations : S3)) continue; if(dual) R = mmul(P, R); return 0; } } } } return 2; } int fpattern::order(const matrix& M) { int cnt = 1; matrix Po = M; while(Po != Id) Po = mmul(Po, M), cnt++; return cnt; } EX int triplet_id = 0; vector<triplet_info> fpattern::find_triplets() { int N = isize(matrices); auto compute_transcript = [&] (int i, int j) { vector<int> indices(N, -1); vector<int> transcript; vector<int> q; int qty = 0; auto visit = [&] (int id) { transcript.push_back(indices[id]); if(indices[id] == -1) { indices[id] = isize(q); q.push_back(id); qty++; } }; visit(0); for(int x=0; x<isize(q); x++) { int at = q[x]; visit(gmul(at, i)); visit(gmul(at, j)); } transcript.push_back(qty); return transcript; }; DEBB(DF_FIELD, ("looking for alternate solutions")); auto orig_transcript = compute_transcript(1, S7); set<vector<int>> transcripts_seen; transcripts_seen.insert(orig_transcript); set<int> conjugacy_classes; vector<int> cc; for(int i=0; i<N; i++) conjugacy_classes.insert(i); for(int i=0; i<N; i++) { if(!conjugacy_classes.count(i)) continue; vector<int> removals; for(int j=0; j<N; j++) { int c = gmul(inverses[j], gmul(i, j)); if(c > i) removals.push_back(c); } for(auto r: removals) conjugacy_classes.erase(r); cc.push_back(i); } DEBB(DF_FIELD, ("conjugacy_classes = ", cc)); vector<triplet_info> tinf; triplet_info ti; ti.i = 1; ti.j = S7; ti.size = orig_transcript.back(); tinf.push_back(ti); for(int i: conjugacy_classes) if(gorder(i) == S7) { DEBB(DF_FIELD, ("checking i=", i)); for(int j=1; j<N; j++) if(gorder(j) == 2 && gorder(gmul(i, j)) == S3) { auto t = compute_transcript(i, j); if(!transcripts_seen.count(t)) { transcripts_seen.insert(t); triplet_info ti; ti.i = i; ti.j = j; ti.size = t.back(); tinf.push_back(ti); } } } DEBB(DF_FIELD, ("solutions found = ", isize(transcripts_seen))); return tinf; } void fpattern::build() { if(WDIM == 3) return; for(int i=0; i<isize(qpaths); i++) { matrix M = strtomatrix(qpaths[i]); qcoords.push_back(M); printf("Solved %s as matrix of order %d\n", qpaths[i].c_str(), order(M)); } matcode.clear(); matrices.clear(); add(Id); if(isize(matrices) != local_group) { printf("Error: rotation crash #1 (%d)\n", isize(matrices)); exit(1); } connections.clear(); for(int i=0; i<(int)matrices.size(); i++) { matrix M = matrices[i]; matrix PM = mmul(P, M); add(PM); if(isize(matrices) % local_group) { printf("Error: rotation crash (%d)\n", isize(matrices)); exit(1); } if(!matcode.count(PM)) { printf("Error: not marked\n"); exit(1); } connections.push_back(matcode[PM]); } DEBB(DF_FIELD, ("Computing inverses...\n")); int N = isize(matrices); DEBB(DF_FIELD, ("Number of heptagons: %d\n", N)); if(WDIM == 3) return; rrf.resize(N); rrf[0] = S7-1; for(int i=0; i<N; i++) rrf[btspin(i,1)] = btspin(rrf[i], 1), rrf[connections[i]] = connections[rrf[i]]; rpf.resize(N); rpf[0] = S7; for(int i=0; i<N; i++) rpf[btspin(i,1)] = btspin(rpf[i], 1), rpf[connections[i]] = connections[rpf[i]]; inverses.resize(N); inverses[0] = 0; for(int i=0; i<N; i++) // inverses[i] = gpow(i, N-1); inverses[btspin(i,1)] = rrf[inverses[i]], // btspin(inverses[i],6), inverses[connections[i]] = rpf[inverses[i]]; int errs = 0; for(int i=0; i<N; i++) if(gmul(i, inverses[i])) errs++; if(errs) printf("errs = %d\n", errs); if(0) for(int i=0; i<isize(matrices); i++) { printf("%5d/%4d", connections[i], inverses[i]); if(i%S7 == S7-1) printf("\n"); } DEBB(DF_FIELD, ("triplet_id = ", triplet_id, " N = ", N)); if(triplet_id) { auto triplets = find_triplets(); if(triplet_id >= 0 && triplet_id < isize(triplets)) { auto ti = triplets[triplet_id]; R = matrices[ti.i]; P = matrices[ti.j]; dynamicval<int> t(triplet_id, 0); build(); DEBB(DF_FIELD, ("triplet built successfully")); return; } } DEBB(DF_FIELD, ("Built.\n")); } int fpattern::getdist(pair<int,bool> a, vector<char>& dists) { if(!a.second) return dists[a.first]; int m = MAXDIST; int ma = dists[a.first]; int mb = dists[connections[btspin(a.first, 3)]]; int mc = dists[connections[btspin(a.first, 4)]]; m = min(m, 1 + ma); m = min(m, 1 + mb); m = min(m, 1 + mc); if(m <= 2 && ma+mb+mc <= m*3-2) return m-1; // special case m = min(m, 2 + dists[connections[btspin(a.first, 2)]]); m = min(m, 2 + dists[connections[btspin(a.first, 5)]]); m = min(m, 2 + dists[connections[btspin(connections[btspin(a.first, 3)], 5)]]); return m; } int fpattern::getdist(pair<int,bool> a, pair<int,bool> b) { if(a.first == b.first) return a.second == b.second ? 0 : 1; if(b.first) a.first = gmul(a.first, inverses[b.first]), b.first = 0; return getdist(a, b.second ? disthex : disthep); } int fpattern::dijkstra(vector<char>& dists, vector<int> indist[MAXDIST]) { int N = isize(matrices); dists.resize(N); for(int i=0; i<N; i++) dists[i] = MAXDIST-1; int maxd = 0; for(int i=0; i<MAXDIST; i++) while(!indist[i].empty()) { int at = indist[i].back(); indist[i].pop_back(); if(dists[at] <= i) continue; maxd = i; dists[at] = i; int lg = MWDIM == 4 ? local_group : S7; for(int q=0; q<lg; q++) { dists[at] = i; if(WDIM == 3) indist[i+1].push_back(gmul(at, local_group)); else if(PURE) // todo-variation: PURE here? indist[i+1].push_back(connections[at]); else { indist[i+2].push_back(connections[at]); indist[i+3].push_back(connections[btspin(connections[at], 2)]); } at = groupspin(at, 1, lg); } } return maxd; } void fpattern::analyze() { if(MWDIM == 4) { /* we need to compute inverses */ int N = isize(matrices); inverses.resize(N); for(int i=0; i<N; i++) { matrix M = matrices[i]; matrix M2 = mpow(M, N-1); inverses[i] = matcode[M2]; } } DEBB(DF_FIELD, ("variation = %d\n", int(variation))); int N = isize(connections); markers.resize(N); vector<int> indist[MAXDIST]; indist[0].push_back(0); int md0 = dijkstra(disthep, indist); if(MWDIM == 4) return; indist[1].push_back(0); indist[1].push_back(connections[3]); indist[1].push_back(connections[4]); indist[2].push_back(connections[btspin(connections[3], 5)]); indist[2].push_back(connections[2]); indist[2].push_back(connections[5]); int md1 = dijkstra(disthex, indist); maxdist = max(md0, md1); otherpole = 0; for(int i=0; i<N; i+=S7) { int mp = 0; for(int q=0; q<S7; q++) if(disthep[connections[i+q]] < disthep[i]) mp++; if(mp == S7) { bool eq = true; for(int q=0; q<S7; q++) if(disthep[connections[i+q]] != disthep[connections[i]]) eq = false; if(eq) { // for(int q=0; q<S7; q++) printf("%3d", disthep[connections[i+q]]); // printf(" (%2d) at %d\n", disthep[i], i); if(disthep[i] > disthep[otherpole]) otherpole = i; // for(int r=0; r<S7; r++) { // printf("Matrix: "); for(int a=0; a<3; a++) for(int b=0; b<3; b++) // printf("%4d", matrices[i+r][a][b]); printf("\n"); // } } } } circrad = 99; for(int i=0; i<N; i++) for(int u=2; u<4; u++) if(disthep[i] < circrad) if(disthep[connections[i]] < disthep[i] && disthep[connections[btspin(i,u)]] < disthep[i]) circrad = disthep[i]; DEBB(DF_FIELD, ("maxdist = %d otherpole = %d circrad = %d\n", maxdist, otherpole, circrad)); matrix PRRR = strtomatrix("PRRR"); matrix PRRPRRRRR = strtomatrix("PRRPRRRRR"); matrix PRRRP = strtomatrix("PRRRP"); matrix PRP = strtomatrix("PRP"); matrix PR = strtomatrix("PR"); matrix Wall = strtomatrix("RRRPRRRRRPRRRP"); wallorder = order(Wall); wallid = matcode[Wall]; DEBB(DF_FIELD, ("wall order = %d\n", wallorder)); #define SETDIST(X, d, it) {int c = matcode[X]; indist[d].push_back(c); if(it == itNone) ; else if(markers[c] && markers[c] != it) markers[c] = itBuggy; else markers[c] = it; } matrix W = Id; for(int i=0; i<wallorder; i++) { SETDIST(W, 0, itAmethyst) W = mmul(W, Wall); } W = P; for(int i=0; i<wallorder; i++) { SETDIST(W, 0, itEmerald) W = mmul(W, Wall); } int walldist = dijkstra(distwall, indist); DEBB(DF_FIELD, ("wall dist = %d\n", walldist)); W = strtomatrix("RRRRPR"); for(int j=0; j<wallorder; j++) { W = mmul(W, Wall); for(int i=0; i<wallorder; i++) { SETDIST(W, 0, itNone) SETDIST(mmul(PRRR, W), 1, itNone) W = mmul(Wall, W); } } dijkstra(distwall2, indist); int rpushid = matcode[PRRPRRRRR]; riverid = 0; for(int i=0; i<N; i++) { int j = i; int ipush = gmul(rpushid, i); for(int k=0; k<wallorder; k++) { if(ipush == j) { DEBB(DF_FIELD, ("River found at %d:%d\n", i, k)); riverid = i; goto riveridfound; } j = gmul(j, wallid); } } riveridfound: ; W = strtomatrix("RRRRPR"); for(int j=0; j<wallorder; j++) { W = mmul(W, Wall); for(int i=0; i<wallorder; i++) { if(i == 7) SETDIST(W, 0, itCoast) if(i == 3) SETDIST(mmul(PRRRP, W), 0, itWhirlpool) W = mmul(Wall, W); } } dijkstra(PURE ? distriver : distflower, indist); W = matrices[riverid]; for(int i=0; i<wallorder; i++) { SETDIST(W, 0, itStatue) W = mmul(W, Wall); } W = mmul(P, W); for(int i=0; i<wallorder; i++) { SETDIST(W, 0, itSapphire) W = mmul(W, Wall); } W = mmul(PRP, matrices[riverid]); for(int i=0; i<wallorder; i++) { SETDIST(W, 1, itShard) W = mmul(W, Wall); } W = mmul(PR, matrices[riverid]); for(int i=0; i<wallorder; i++) { SETDIST(W, 1, itGold) W = mmul(W, Wall); } int riverdist = dijkstra(PURE ? distflower : distriver, indist); DEBB(DF_FIELD, ("river dist = %d\n", riverdist)); for(int i=0; i<isize(matrices); i++) if(distflower[i] == 0) { distflower0 = inverses[i]+1; break; } if(!PURE) { W = matrices[riverid]; for(int i=0; i<wallorder; i++) { SETDIST(W, 0, itStatue) W = mmul(W, Wall); } W = mmul(PR, matrices[riverid]); for(int i=0; i<wallorder; i++) { SETDIST(W, 0, itGold) W = mmul(W, Wall); } W = mmul(P, matrices[riverid]); for(int i=0; i<wallorder; i++) { SETDIST(W, 1, itSapphire) W = mmul(W, Wall); } dijkstra(distriverleft, indist); W = mmul(PRP, matrices[riverid]); for(int i=0; i<wallorder; i++) { SETDIST(W, 0, itShard) W = mmul(W, Wall); } W = mmul(P, matrices[riverid]); for(int i=0; i<wallorder; i++) { SETDIST(W, 0, itSapphire) W = mmul(W, Wall); } W = matrices[riverid]; for(int i=0; i<wallorder; i++) { SETDIST(W, 1, itStatue) W = mmul(W, Wall); } dijkstra(distriverright, indist); } else { W = strtomatrix("RRRRPR"); for(int j=0; j<wallorder; j++) { W = mmul(W, Wall); for(int i=0; i<wallorder; i++) { if(i == 7) SETDIST(W, 0, itCoast) W = mmul(Wall, W); } } dijkstra(distriverleft, indist); W = strtomatrix("RRRRPR"); for(int j=0; j<wallorder; j++) { W = mmul(W, Wall); for(int i=0; i<wallorder; i++) { if(i == 3) SETDIST(mmul(PRRRP, W), 0, itWhirlpool) W = mmul(Wall, W); } } dijkstra(distriverright, indist); } DEBB(DF_FIELD, ("wall-river distance = %d\n", distwall[riverid])); DEBB(DF_FIELD, ("river-wall distance = %d\n", distriver[0])); } int fpattern::orderstats() { int N = isize(matrices); #define MAXORD 10000 int ordcount[MAXORD]; int ordsample[MAXORD]; for(int i=0; i<MAXORD; i++) ordcount[i] = 0; for(int i=0; i<N; i++) { int cnt = order(matrices[i]); if(cnt < MAXORD) { if(!ordcount[cnt]) ordsample[cnt] = i; ordcount[cnt]++; } } printf("Listing:\n"); for(int i=0; i<MAXORD; i++) if(ordcount[i]) printf("Found %4d matrices of order %3d: %s\n", ordcount[i], i, decodepath(ordsample[i]).c_str()); return ordsample[Prime]; } void fpattern::findsubpath() { int N = isize(matrices); for(int i=1; i<N; i++) if(gpow(i, Prime) == 0) { subpathid = i; subpathorder = Prime; DEBB(DF_FIELD, ("Subpath found: %s\n", decodepath(i).c_str())); return; } } fpattern *fp43; EX void info() { fpattern fp(0); int cases = 0, hard = 0; for(int p=0; p<500; p++) { fp.Prime = p; if(fp.solve() == 0) { printf("%4d: wsquare=%d cs=%d sn=%d ch=%d sh=%d dual=%d\n", p, fp.wsquare, fp.cs, fp.sn, fp.ch, fp.sh, fp.dual); cases++; if(!fp.easy(fp.cs) || !fp.easy(fp.sn) || !fp.easy(fp.ch) || !fp.easy(fp.sn)) hard++; #ifndef EASY neasy = 0; #endif if(WDIM == 3) continue; fp.build(); #ifndef EASY printf("Not easy: %d\n", neasy); #endif int N = isize(fp.matrices); int left = N / fp.Prime; printf("Prime decomposition: %d = %d", N, fp.Prime); for(int p=2; p<=left; p++) while(left%p == 0) printf("*%d", p), left /= p; printf("\n"); printf("Order of RRP is: %d\n", fp.order(fp.strtomatrix("RRP"))); printf("Order of RRRP is: %d\n", fp.order(fp.strtomatrix("RRRP"))); printf("Order of RRRPRRRRRPRRRP is: %d\n", fp.order(fp.strtomatrix("RRRPRRRRRPRRRP"))); } } printf("cases found = %d (%d hard)\n", cases, hard); } EX fpattern current_quotient_field = fpattern(0); EX fpattern fp_invalid = fpattern(0); EX bool quotient_field_changed; // these strings contain \x00 #define STR(x) string(x, sizeof(x)) EX struct fpattern& getcurrfp() { if(fake::in()) return *FPIU(&getcurrfp()); if(geometry == gFieldQuotient && quotient_field_changed) return current_quotient_field; if(geometry == gSpace535) { // 120 cells, hash = 9EF7A9C4 static fpattern fp(0); if(use_rule_fp) { fp.Prime = 5; fp.force_hash = 0xDCC3CACEu; fp.solve(); } else { fp.Prime = 5; fp.force_hash = 0x9EF7A9C4u; fp.solve(); } return fp; } if(geometry == gSpace534) { // 260 cells, hash = 72414D0C (not 0C62E214) static fpattern fp(0); if(fp.Prime) return fp; // fp.Prime = 5; fp.force_hash = 0x72414D0C; fp.solve(); if(use_rule_fp) { fp.Prime = 11; fp.force_hash = 0x5FC4CFF0u; fp.solve(); } else { shstream ins(STR("\x05\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\xfc\xff\xff\xff\x01\x00\x00\x00\x04\x00\x00\x00\xfc\xff\xff\xff\x04\x00\x00\x00\xfe\xff\xff\xff\x00\x00\x00\x00\x01\x00\x00\x00\xfe\xff\xff\xff\x04\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xfc\xff\xff\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xfc\xff\xff\xff\x02\x00\x00\x00\x00\x00\x00\x00\xfc\xff\xff\xff\x01\x00\x00\x00\xfd\xff\xff\xff\x00\x00\x00\x00\x02\x00\x00\x00\xfd\xff\xff\xff\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00")); hread_fpattern(ins, fp); } return fp; } if(geometry == gSpace435) { // 650 cells, hash = EB201050 static fpattern fp(0); if(fp.Prime) return fp; // fp.Prime = 5; fp.force_hash = 0xEB201050; fp.solve(); // what is 0x72414D0C?? if(use_rule_fp) { fp.Prime = 11; fp.force_hash = 0x65CE0C00u; fp.solve(); } else { shstream ins(STR("\x05\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xfc\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xfc\xff\xff\xff\x04\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\xff\xff\xff\xff\x02\x00\x00\x00\x03\x00\x00\x00\x03\x00\x00\x00\xfd\xff\xff\xff\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xfc\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xfd\xff\xff\xff\xfd\xff\xff\xff\x00\x00\x00\x00\xfd\xff\xff\xff\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\xfd\xff\xff\xff\x03\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00")); hread_fpattern(ins, fp); } return fp; } if(geometry == gSpace436) { static fpattern fp(0); if(fp.Prime) return fp; if(use_rule_fp) { fp.Prime = 2; fp.force_hash = 0x235F7508u; fp.solve(); } else { // FF82A214 shstream ins(STR("\x05\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\xfd\xff\xff\xff\x00\x00\x00\x00\xfe\xff\xff\xff\xfd\xff\xff\xff\x01\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\xfd\xff\xff\xff\x02\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\xfc\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xfd\xff\xff\xff\xfd\xff\xff\xff\x00\x00\x00\x00\xfd\xff\xff\xff\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\xfd\xff\xff\xff\x03\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00")); hread_fpattern(ins, fp); } return fp; } if(geometry == gSpace336) { static fpattern fp(0); if(fp.Prime) return fp; if(use_rule_fp) { fp.Prime = 3; fp.force_hash = 0xD29C2418u; fp.solve(); } else { // fp.Prime = 7; fp.force_hash = 0xE3F6B7BCu; fp.solve(); shstream ins(STR("\x07\x00\x00\x00\x03\x00\x00\x00\xfa\xff\xff\xff\x02\x00\x00\x00\x03\x00\x00\x00\x06\x00\x00\x00\x02\x00\x00\x00\xfe\xff\xff\xff\xfb\xff\xff\xff\xfc\xff\xff\xff\x03\x00\x00\x00\xfb\xff\xff\xff\xfd\xff\xff\xff\xfb\xff\xff\xff\x01\x00\x00\x00\xfd\xff\xff\xff\xfe\xff\xff\xff\xfd\xff\xff\xff\x03\x00\x00\x00\x00\x00\x00\x00\xfd\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfc\xff\xff\xff\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xfa\xff\xff\xff\xfb\xff\xff\xff\x00\x00\x00\x00\xfa\xff\xff\xff\x02\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\xfb\xff\xff\xff\x06\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00")); hread_fpattern(ins, fp); } return fp; } if(geometry == gSpace344) { // 600 cells in 558C8ED0 // 2400 cells in AF042EA8 // 2600 cells in D26948E0 // 2600 cells in EC29DCEC static fpattern fp(0); if(fp.Prime) return fp; if(use_rule_fp) { fp.Prime = 3; fp.force_hash = 0xB23AF1F4u; fp.solve(); } else { fp.Prime = 5; fp.force_hash = 0x558C8ED0u; fp.solve(); } return fp; // 4900 cells in CDCC7860 (7) } if(geometry == gSpace536) { static fpattern fp(0); if(fp.Prime) return fp; // 130 cells in 3BA5C5A4 // 260 cells in 9FDE7B38 if(use_rule_fp) { fp.Prime = 5; fp.force_hash = 0x61385498u; fp.solve(); } else { fp.Prime = 5; fp.force_hash = 0x9FDE7B38u; fp.solve(); } return fp; } if(geometry == gSpace345) { static fpattern fp(0); if(fp.Prime) return fp; // 30 cells in 02ADCAA4 (3^2) // 650 cells in 7EFE8D98 (5^2) // 55 cells in F447F75C (11) if(use_rule_fp) { fp.Prime = 3; fp.force_hash = 0xF978E264u; fp.solve(); } else { fp.Prime = 11; fp.force_hash = 0xF447F75Cu; fp.solve(); } return fp; } if(geometry == gSpace353) { static fpattern fp(0); if(fp.Prime) return fp; // 130 cells in 1566EBAC (5^2) // 11 cells in 5A2E2B88 (11) fp.Prime = 11; fp.force_hash = 0x5A2E2B88u; fp.solve(); return fp; } if(geometry == gSpace354) { static fpattern fp(0); // fp.Prime = 11; fp.force_hash = 0x363D8DA4u; fp.solve(); fp.Prime = 5; fp.force_hash = 0x58A8E850u; fp.solve(); return fp; } if(geometry == gCubeTiling) { static fpattern fp(2); return fp; } if(!hyperbolic) return fp_invalid; if(WDIM == 3 && !quotient && !mhybrid && !bt::in()) { static fpattern fp(0); if(fp.Prime) return fp; for(int p=2; p<20; p++) { fp.Prime = p; if(!fp.solve()) break; } DEBB(DF_FIELD, ("set prime = ", fp.Prime)); return fp; } if(S7 == 8 && S3 == 3 && !bt::in()) { static fpattern fp(17); return fp; } if(S7 == 5 && S3 == 4 && !bt::in()) { static fpattern fp(11); return fp; } if(S7 == 6 && S3 == 4 && !bt::in()) { static fpattern fp(13); return fp; } if(S7 == 7 && S3 == 4 && !bt::in()) { static fpattern fp(13); return fp; } if(sphere || euclid) return fp_invalid; if(S7 == 7 && S3 == 3 && !bt::in()) { if(!fp43) fp43 = new fpattern(43); return *fp43; } return fp_invalid; } #undef STR // todo undefined behavior EX int subpathid = -1; EX int subpathorder = -1; // extra information for field quotient extra configuration EX vector<fgeomextra> fgeomextras = { fgeomextra(gNormal, 4), fgeomextra(gOctagon, 1), fgeomextra(g45, 1), fgeomextra(g46, 5), fgeomextra(g47, 1), fgeomextra(gSchmutzM3, 0), /* fgeomextra(gSphere, 0), fgeomextra(gSmallSphere, 0), -> does not find the prime fgeomextra(gEuclid, 0), fgeomextra(gEuclidSquare, 0), fgeomextra(gTinySphere, 0) */ }; EX int current_extra = 0; EX void nextPrime(fgeomextra& ex) { dynamicval<eGeometry> g(geometry, ex.base); dynamicval<int> t(triplet_id, 0); int nextprime; if(isize(ex.primes)) nextprime = ex.primes.back().p + 1; else nextprime = 2; while(true) { fieldpattern::fpattern fp(0); fp.Prime = nextprime; if(fp.solve() == 0) { fp.build(); int cells = isize(fp.matrices) / S7; ex.primes.emplace_back(primeinfo{nextprime, cells, (bool) fp.wsquare}); ex.dualval.emplace_back(fp.dual); break; } nextprime++; } } EX void nextPrimes(fgeomextra& ex) { while(isize(ex.primes) < 6) nextPrime(ex); } EX void enableFieldChange() { fgeomextra& gxcur = fgeomextras[current_extra]; fieldpattern::quotient_field_changed = true; nextPrimes(gxcur); dynamicval<eGeometry> g(geometry, gFieldQuotient); ginf[geometry].g = ginf[gxcur.base].g; ginf[geometry].sides = ginf[gxcur.base].sides; ginf[geometry].vertex = ginf[gxcur.base].vertex; ginf[geometry].distlimit = ginf[gxcur.base].distlimit; ginf[geometry].tiling_name = ginf[gxcur.base].tiling_name; ginf[geometry].default_variation = ginf[gxcur.base].default_variation; ginf[geometry].flags = qFIELD | qANYQ | qCLOSED; fieldpattern::current_quotient_field.init(gxcur.primes[gxcur.current_prime_id].p); } EX eGeometry underlying_geometry; EX void field_from_current() { auto& go = ginf[geometry]; underlying_geometry = geometry; dynamicval<eGeometry> g(geometry, gFieldQuotient); auto& gg = ginf[geometry]; gg.sides = go.sides; gg.vertex = go.vertex; gg.distlimit = go.distlimit; gg.tiling_name = go.tiling_name; gg.flags = go.flags | qANYQ | qFIELD | qCLOSED; gg.g = go.g; gg.default_variation = go.default_variation; fieldpattern::quotient_field_changed = true; } #if CAP_THREAD && MAXMDIM >= 4 EX map<string, discovery> discoveries; void discovery::activate() { if(!discoverer) { discoverer = std::make_shared<std::thread> ( [this] { for(int p=2; p<100; p++) { experiment.Prime = p; experiment.solve(); if(stop_it) break; } }); } if(is_suspended) { if(1) { std::unique_lock<std::mutex> lk(lock); is_suspended = false; } cv.notify_one(); } } void discovery::discovered() { std::unique_lock<std::mutex> lk(lock); auto& e = experiment; hashes_found[e.hashv] = make_tuple(e.Prime, e.wsquare, e.R, e.P, e.X, isize(e.matrices) / e.local_group); } void discovery::suspend() { is_suspended = true; } void discovery::check_suspend() { std::unique_lock<std::mutex> lk(lock); if(is_suspended) cv.wait(lk, [this] { return !is_suspended; }); } void discovery::schedule_destruction() { stop_it = true; } discovery::~discovery() { schedule_destruction(); if(discoverer) discoverer->join(); } #endif int hk = #if CAP_THREAD #if MAXMDIM >= 4 + addHook(hooks_on_geometry_change, 100, [] { for(auto& d:discoveries) if(!d.second.is_suspended) d.second.suspend(); }) + addHook(hooks_final_cleanup, 100, [] { for(auto& d:discoveries) { d.second.schedule_destruction(); if(d.second.is_suspended) d.second.activate(); } discoveries.clear(); }) #endif #endif #if CAP_COMMANDLINE + addHook(hooks_args, 0, [] { using namespace arg; if(0) ; else if(argis("-q3-limitsq")) { shift(); limitsq = argi(); } else if(argis("-q3-limitp")) { shift(); limitp = argi(); } else if(argis("-q3-limitv")) { shift(); limitv = argi(); } else return 1; return 0; }) #endif + 0; EX purehookset hooks_on_geometry_change; EX int field_celldistance(cell *c1, cell *c2) { if(geometry != gFieldQuotient) return DISTANCE_UNKNOWN; if(GOLDBERG) return DISTANCE_UNKNOWN; auto v1 =fieldpattern::fieldval(c1); auto v2 =fieldpattern::fieldval(c2); int d = currfp.getdist(v1, v2); return d; } EX } #define currfp fieldpattern::getcurrfp() EX int currfp_gmul(int a, int b) { return currfp.gmul(a,b); } EX int currfp_inverses(int i) { return currfp.inverses[i]; } EX int currfp_distwall(int i) { return currfp.distwall[i]; } EX int currfp_n() { return isize(currfp.matrices); } EX int currfp_get_R() { return currfp.matcode[currfp.R]; } EX int currfp_get_P() { return currfp.matcode[currfp.P]; } EX int currfp_get_X() { return currfp.matcode[currfp.X]; } EX void hread_fpattern(hstream& hs, fieldpattern::fpattern& fp) { hread(hs, fp.Prime); hread(hs, fp.wsquare); hread(hs, fp.P); hread(hs, fp.R); hread(hs, fp.X); fp.set_field(fp.Prime, fp.wsquare); #if MAXMDIM >= 4 fp.generate_all3(); #endif } EX void hwrite_fpattern(hstream& hs, fieldpattern::fpattern& fp) { hwrite(hs, fp.Prime); hwrite(hs, fp.wsquare); hwrite(hs, fp.P); hwrite(hs, fp.R); hwrite(hs, fp.X); } } #endif