From 5382038c4e3e1d445b3c1a910bbff1df9f93656a Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Thu, 16 Jan 2020 16:39:18 +0100 Subject: [PATCH] field quotient discovery --- fieldpattern.cpp | 123 +++++++++++++++++++++++++++++++++++++++++++---- geom-exp.cpp | 82 +++++++++++++++++++++++++++---- sysconfig.h | 9 ++++ system.cpp | 8 ++- 4 files changed, 203 insertions(+), 19 deletions(-) diff --git a/fieldpattern.cpp b/fieldpattern.cpp index 80be1d1e..324a0ddb 100644 --- a/fieldpattern.cpp +++ b/fieldpattern.cpp @@ -11,6 +11,10 @@ namespace hr { EX namespace fieldpattern { +int limitsq = 10; +int limitp = 10000; +int limitv = 100000; + #if HDR #define currfp fieldpattern::getcurrfp() @@ -280,6 +284,7 @@ struct fpattern { fpattern(int p) { force_hash = 0; + dis = nullptr; if(!p) return; init(p); } @@ -299,8 +304,35 @@ struct fpattern { void add1(const matrix& M, const transmatrix& Full); vector generate_isometries3(); int solve3(); - void generate_all3(); + bool generate_all3(); + + // transmatrix full_R, full_P, full_X; + + #if CAP_THREAD + struct discovery *dis; + #endif }; + +#if CAP_THREAD +struct discovery { + fpattern experiment; + std::shared_ptr discoverer; + std::mutex lock, slock; + bool is_suspended; + bool stop_it; + + map > hashes_found; + discovery() : experiment(0) { is_suspended = false; stop_it = false; experiment.dis = this; } + + void activate(); + void suspend(); + void check_suspend(); + void schedule_destruction(); + void discovered(); + ~discovery(); + }; +#endif + #endif bool fpattern::check_order(matrix M, int req) { @@ -365,7 +397,12 @@ vector fpattern::generate_isometries3() { for(T[2][1]=low; T[2][1]check_suspend(); + if(dis && dis->stop_it) return res; + #endif + for(T[0][2]=low; T[0][2] fpattern::generate_isometries3() { if(rowcol(3, 1) == 0) if(rowcol(3, 2) == 0) res.push_back(T); + if(isize(res) > limitp) return res; + } return res; } @@ -426,7 +465,7 @@ unsigned fpattern::compute_hash() { return hashv; } -void fpattern::generate_all3() { +bool fpattern::generate_all3() { matrices.clear(); matcode.clear(); add1(Id); @@ -440,13 +479,15 @@ void fpattern::generate_all3() { matrix E = mmul(matrices[i], P); if(!matcode.count(E)) for(int j=0; j= limitv) { println(hlog, "limitv exceeded"); return false; } } unsigned hashv = compute_hash(); DEBB(DF_FIELD, ("all = ", isize(matrices), "/", local_group, " = ", isize(matrices) / local_group, " hash = ", hashv, " count = ", ++hash_found[hashv])); + return true; } int fpattern::solve3() { - if(!wsquare) return 0; + reg3::generate_cellrotations(); reg3::construct_relations(); DEBB(DF_FIELD, ("generating isometries for ", Field)); @@ -476,7 +517,11 @@ int fpattern::solve3() { for(auto& xX: possible_X) for(auto& xP: possible_P) if(check_order(mmul(xP, xX), reg3::xp_order)) - for(auto& xR: possible_R) if(check_order(mmul(xR, xX), reg3::rx_order)) { // if(xR[0][0] == 1 && xR[0][1] == 0) { + for(auto& xR: possible_R) if(check_order(mmul(xR, xX), reg3::rx_order)) { // if(xR[0][0] == 1 && xR[0][1] == 0) + #if CAP_THREAD + if(dis) dis->check_suspend(); + if(dis && dis->stop_it) return 0; + #endif auto by = [&] (char ch) -> matrix& { return ch == 'X' ? xX : ch == 'R' ? xR : xP; }; for(int i=0; idiscovered(); continue; } + #endif if(force_hash && compute_hash() != force_hash) continue; cmb++; goto ok; @@ -526,7 +574,7 @@ int fpattern::solve() { } else wsquare = 0; if(WDIM == 3) { - if(dual == 0) { + if(dual == 0 && (Prime <= limitsq || pw == 1)) { int s = solve3(); if(s) return 0; } @@ -729,7 +777,6 @@ void fpattern::analyze() { matrix M = matrices[i]; matrix M2 = mpow(M, N-1); inverses[i] = matcode[M2]; - println(hlog, mmul(M, M2)); } } @@ -1164,9 +1211,67 @@ EX void field_from_current() { gg.flags = go.flags | qANYQ | qFIELD | qBOUNDED; gg.g = go.g; gg.default_variation = go.default_variation; - fieldpattern::quotient_field_changed = true; + fieldpattern::quotient_field_changed = true; } +#if CAP_THREAD +EX map discoveries; + +void discovery::activate() { + if(!discoverer) { + discoverer = std::make_shared ( [this] { + for(int p=2; p<100; p++) { + experiment.Prime = p; + experiment.solve(); + if(stop_it) break; + } + }); + slock.lock(); + } + if(is_suspended) { + is_suspended = false; + lock.unlock(); + slock.unlock(); + } + } + +void discovery::discovered() { + slock.unlock(); + lock.lock(); + auto& e = experiment; + hashes_found[e.compute_hash()] = make_tuple(e.Prime, e.wsquare, e.R, e.P, e.X, isize(e.matrices) / e.local_group); + lock.unlock(); + slock.lock(); + } + +void discovery::suspend() { is_suspended = true; lock.lock(); slock.lock(); } + +void discovery::check_suspend() { slock.unlock(); lock.lock(); lock.unlock(); slock.lock(); } + +void discovery::schedule_destruction() { stop_it = true; } +discovery::~discovery() { schedule_destruction(); if(discoverer) discoverer->join(); } +#endif + +int hk = +#if CAP_THREAD + + addHook(on_geometry_change, 100, [] { for(auto& d:discoveries) if(!d.second.is_suspended) d.second.suspend(); }) + + addHook(final_cleanup, 100, [] { + for(auto& d:discoveries) { d.second.schedule_destruction(); if(d.second.is_suspended) d.second.activate(); } + discoveries.clear(); + }) +#endif + + 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; + }); + +EX purehookset on_geometry_change; + EX } #define currfp fieldpattern::getcurrfp() diff --git a/geom-exp.cpp b/geom-exp.cpp index 48e3204b..7afbe1cb 100644 --- a/geom-exp.cpp +++ b/geom-exp.cpp @@ -404,6 +404,65 @@ EX string dim_name() { return " (" + its(WDIM) + "D)"; } +#if CAP_THREAD +EX void showQuotientConfig3() { + + using namespace fieldpattern; + gamescreen(2); + dialog::init(XLAT("field quotient")); + + auto& ds = discoveries[cginf.tiling_name]; + + if(!ds.discoverer) { + dialog::addItem("start discovery", 's'); + dialog::add_action([&ds] { ds.activate(); }); + } + else if(ds.is_suspended) { + dialog::addItem("resume discovery", 's'); + dialog::add_action([&ds] { ds.activate(); }); + } + else { + dialog::addItem("suspend discovery", 's'); + dialog::add_action([&ds] { ds.suspend(); }); + } + + auto& e = ds.experiment; + if(!e.Prime) + dialog::addBreak(100); + else { + string s = its(e.Prime); + if(e.wsquare) s += "²"; + dialog::addInfo(s); + } + + dialog::addBreak(100); + + if(!ds.is_suspended) ds.lock.lock(); + auto&l = ds.hashes_found; + for(auto& v: l) { + char x = 'a'; + string s = XLAT("#%1, cells: %2", itsh(v.first), its(get<5>(v.second))); + dialog::addItem(s, x++); + dialog::add_action([&v] { + stop_game(); + int tmp; + tie(currfp.Prime, currfp.wsquare, currfp.R, currfp.P, currfp.X, tmp) = v.second; + currfp.Field = currfp.wsquare ? currfp.Prime * currfp.Prime : currfp.Prime; + currfp.generate_all3(); + currfp.analyze(); + start_game(); + }); + } + if(!ds.is_suspended) ds.lock.unlock(); + + dialog::addBreak(100); + + dialog::addBack(); + + dialog::display(); + } +#endif + EX string geometry_name() { switch(ginf[geometry].cclass) { case gcHyperbolic: @@ -459,12 +518,17 @@ EX void select_quotient_screen() { g == geometry, key++); dialog::add_action([g] { if(g == gFieldQuotient && WDIM == 3) { - stop_game(); - fieldpattern::field_from_current(); - set_geometry(gFieldQuotient); - for(int p=2;; p++) { currfp.Prime = p; currfp.force_hash = 0; if(!currfp.solve()) break; } - println(hlog, "set prime = ", currfp.Prime); - start_game(); + if(geometry != gFieldQuotient) { + stop_game(); + fieldpattern::field_from_current(); + set_geometry(gFieldQuotient); + for(int p=2;; p++) { currfp.Prime = p; currfp.force_hash = 0; if(!currfp.solve()) break; } + println(hlog, "set prime = ", currfp.Prime); + start_game(); + } + #if CAP_THREAD + pushScreen(showQuotientConfig3); + #endif } else if(g == gFieldQuotient) pushScreen(showQuotientConfig); @@ -503,12 +567,12 @@ EX void select_quotient() { println(hlog, "choices = ", choices); - if(isize(choices) > 2) + if(isize(choices) > 1) pushScreen(select_quotient_screen); - else if(isize(choices) > 1) { + /* else if(isize(choices) > 1) { set_geometry(choices[choices[0] == geometry ? 1 : 0]); start_game(); - } + } */ else addMessage("No quotient spaces avialable in the current tiling."); } diff --git a/sysconfig.h b/sysconfig.h index 3bb9436f..14caf483 100644 --- a/sysconfig.h +++ b/sysconfig.h @@ -73,6 +73,10 @@ #define CAP_XGD (ISANDROID || ISFAKEMOBILE) #endif +#ifndef CAP_THREAD +#define CAP_THREAD (!ISMOBILE && !ISWEB) +#endif + #define CAP_FRAMELIMIT (!ISMOBWEB) #if ISMOBILE @@ -421,6 +425,11 @@ extern "C" { #include #include +#if CAP_THREAD +#include +#include +#endif + #ifdef USE_UNORDERED_MAP #include #include diff --git a/system.cpp b/system.cpp index 273f8d96..6d0d25ee 100644 --- a/system.cpp +++ b/system.cpp @@ -1201,9 +1201,12 @@ eModel default_model() { if(GDIM == 3) return mdPerspective; return mdDisk; } - + +EX purehookset on_geometry_change; + EX void set_geometry(eGeometry target) { bool was_default = pmodel == default_model(); + callhooks(on_geometry_change); if(geometry != target) { int old_DIM = GDIM; stop_game(); @@ -1492,6 +1495,8 @@ EX void initAll() { polygonal::solve(); } +EX purehookset final_cleanup; + EX void finishAll() { achievement_final(!items[itOrbSafety]); @@ -1505,6 +1510,7 @@ EX void finishAll() { #endif achievement_close(); + callhooks(final_cleanup); }