// Hyperbolic Rogue -- implementation of the quotient geometries based on fields // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details namespace hr { namespace fieldpattern { extern int subpathid; extern int subpathorder; bool isprime(int n) { for(int k=2; k= 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); } matrix mmul(const matrix& A, const matrix& B) { matrix res; for(int i=0; i<3; i++) for(int k=0; k<3; k++) { #ifdef EASY res[i][k] = (mul(A[i][0], B[0][k]) + mul(A[i][1], B[1][k]) + mul(A[i][2], B[2][k])) % Prime; #else int t=0; for(int j=0; j<3; j++) t = add(t, mul(A[i][j], B[j][k])); res[i][k] = t; #endif } return res; } map matcode; vector matrices; vector qpaths; vector qcoords; matrix Id, R, P; 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; vector rrf; // rrf[i] equals gmul(i, S7-1) vector rpf; // rpf[i] equals gmul(i, S7) 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)]; } pair gmul(pair a, int b) { return make_pair(gmul(a.first,b), a.second); } int order(const matrix& M) { int cnt = 1; matrix Po = M; while(Po != Id) Po = mmul(Po, M), cnt++; return cnt; } 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() { for(int a=0; a<3; a++) for(int b=0; b<3; b++) Id[a][b] = a==b?1:0; if(!isprime(Prime)) { return 1; } for(int pw=1; pw<3; pw++) { if(pw>3) break; Field = pw==1? Prime : Prime*Prime; if(pw == 2) { for(wsquare=1; wsquare disthep; vector disthex; vector distwall, distriver, distwall2, distriverleft, distriverright, distflower; int distflower0; vector markers; int getdist(pair a, vector& dists) { if(!a.second) return dists[a.first]; int m = 60; 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 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 maxdist, otherpole, circrad, wallid, wallorder, riverid; int dijkstra(vector& dists, vector indist[64]) { int N = connections.size(); dists.resize(N); for(int i=0; i indist[64]; indist[0].push_back(0); int md0 = dijkstra(disthep, indist); 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 fgeomextras = { fgeomextra(gNormal, 3), fgeomextra(gOctagon, 1), fgeomextra(g45, 0), fgeomextra(g46, 3), fgeomextra(g47, 0), /* fgeomextra(gSphere, 0), fgeomextra(gSmallSphere, 0), -> does not find the prime fgeomextra(gEuclid, 0), fgeomextra(gEuclidSquare, 0), fgeomextra(gTinySphere, 0) */ }; int current_extra = 0; void nextPrime(fgeomextra& ex) { dynamicval g(geometry, ex.base); 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 = fp.matrices.size() / S7; ex.primes.emplace_back(primeinfo{nextprime, cells, (bool) fp.wsquare}); break; } nextprime++; } } void nextPrimes(fgeomextra& ex) { while(isize(ex.primes) < 4) nextPrime(ex); } void enableFieldChange() { fgeomextra& gxcur = fgeomextras[current_extra]; fieldpattern::quotient_field_changed = true; nextPrimes(gxcur); dynamicval g(geometry, gFieldQuotient); ginf[geometry].sides = ginf[gxcur.base].sides; ginf[geometry].vertex = ginf[gxcur.base].vertex; ginf[geometry].distlimit = ginf[gxcur.base].distlimit; fieldpattern::current_quotient_field.init(gxcur.primes[gxcur.current_prime_id].p); } } #define currfp fieldpattern::getcurrfp() int currfp_gmul(int a, int b) { return currfp.gmul(a,b); } int currfp_inverses(int i) { return currfp.inverses[i]; } int currfp_distwall(int i) { return currfp.distwall[i]; } }