// 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 primes; vector 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, MAXMDIM> { bool operator == (const matrix& B) const { for(int i=0; i= 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 0) tp += val; else tn += val; } tp %= Prime; tn %= Prime; if(tp && tn) err++; t = tp + tn; #else for(int j=0; j matcode; vector matrices; vector qpaths; vector 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 connections; vector inverses; // NYI in 3D // 2D only vector rrf; // rrf[i] equals gmul(i, rotations-1) vector 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 gmul(pair 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 disthep; vector disthex; vector distwall, distriver, distwall2, distriverleft, distriverright, distflower; int distflower0; vector markers; int getdist(pair a, vector& dists); int getdist(pair a, pair b); int dijkstra(vector& dists, vector 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 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 fullv; void add1(const matrix& M); void add1(const matrix& M, const transmatrix& Full); vector generate_isometries3(); int solve3(); bool generate_all3(); #if CAP_THREAD struct discovery *dis; #endif #endif vector find_triplets(); void generate_quotientgroup(); }; #if CAP_THREAD && MAXMDIM >= 4 struct discovery { fpattern experiment; std::shared_ptr discoverer; std::mutex lock; std::condition_variable cv; bool is_suspended; bool stop_it; map > 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 fpattern::generate_isometries() { matrix T = Id; int low = wsquare ? 1-Prime : 0; vector 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]= 4 vector fpattern::generate_isometries3() { matrix T = Id; int low = wsquare ? 1-Prime : 0; vector 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]= 4 if(dis) dis->check_suspend(); if(dis && dis->stop_it) return res; #endif for(T[0][2]=low; T[0][2] 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 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= 4 bool fpattern::generate_all3() { reg3::generate_fulls(); err = 0; matrices.clear(); matcode.clear(); add1(Id); fullv = {hr::Id}; for(int i=0; i= 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= local_group) j = gmul(j, i), p++; if(j == 0 && p > best_p) { bool okay = true; vector visited(MS, false); for(int ii=0; ii 1) { vector new_id(MS, -1); vector orig_id(MS, -1); vector new_matrices; int nv = 0; for(int i=0; i 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; a3) break; Field = pw==1? Prime : Prime*Prime; if(pw == 2) { for(wsquare=1; wsquare= 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 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 sqrts(Prime, 0); for(int k=1-Prime; k sqrts(Field); for(int k=0; k fpattern::find_triplets() { int N = isize(matrices); auto compute_transcript = [&] (int i, int j) { vector indices(N, -1); vector transcript; vector 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> transcripts_seen; transcripts_seen.insert(orig_transcript); set conjugacy_classes; vector cc; for(int i=0; i removals; for(int j=0; j i) removals.push_back(c); } for(auto r: removals) conjugacy_classes.erase(r); cc.push_back(i); } DEBB(DF_FIELD, ("conjugacy_classes = ", cc)); vector 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 a, vector& 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 a, pair 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& dists, vector indist[MAXDIST]) { int N = isize(matrices); dists.resize(N); for(int i=0; i 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 disthep[otherpole]) otherpole = i; // for(int r=0; r= 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