hyperrogue/rogueviz/dhrg/dynamic.cpp

147 lines
4.4 KiB
C++

// computing pairs of vertices in each distance using dynamic programming (as described in the paper)
namespace dhrg {
bool segmentValid(mycell *cl, mycell *cr) {
if(cl == cr) return true;
mycell *c1 = cl;
int d = 0;
for(; d<7 && c1 != cr; d++) {
c1->build(); c1 = c1->grightsibling();
}
if(d == 7) return false;
cr->build();
cl = cl->grightsibling()->gleftchild();
cr = cr->gleftchild();
return segmentValid(cl, cr);
}
vector<segment*> all_child_segments(segment *s) {
vector<segment*> res;
for(auto m1: allchildren(s->left, -1))
for(auto m2: allchildren(s->right, +1))
if(segmentValid(m1, m2))
res.push_back(getsegment(m1,m2,0,true));
return res;
}
// returns 0 if not in segment, 1-based index if in segment
int insegment(mycell *mc, segment *s1) {
mycell *l = s1->left;
int i = 1;
while(true) {
if(l == mc) return i;
if(l == s1->right) return 0;
l->build(); l = l->grightsibling();
i++;
}
}
int segmentcode(segment *s) {
mycell *l = s->left;
int code = 0;
while(true) {
code += l->gettype();
if(l == s->right) return code;
l->build(); l = l->grightsibling();
code *= 8;
}
}
int compute_descendants(segment *s, int d) {
auto memokey = make_tuple(segmentcode(s), d);
static map<decltype(memokey), int> mem;
if(mem.count(memokey)) return mem[memokey];
if(d == 0) return s->left == s->right ? 1 : 0;
int total = 0;
for(auto s1: all_child_segments(s))
total += compute_descendants(s1, d-1);
return mem[memokey] = total;
}
// returns 0 if segments are not crossing, positive number if crossing
// for segments with equal codes, equal numbers = the same way of crossing
int segmentcross(segment *s1, segment *s2) {
int i1;
i1 = insegment(s1->left, s2);
if(i1) return 4*i1+1;
i1 = insegment(s1->right, s2);
if(i1) return 4*i1+2;
i1 = insegment(s2->left, s1);
if(i1) return 4*i1+3;
i1 = insegment(s2->right, s1);
if(i1) return 4*i1+4;
return 0;
}
set<int> allsegments;
int compute_in_dist(segment *s1, segment *s2, int d1, int d2, int dex) {
// if(d1 + d2 + 4 < dex) return 0;
int d = -segmentcross(s1,s2);
if(!d) d = segmentdist(s1, s2, 0);
if(d > 2 || d1 == 0 || d2 == 0) {
if(d < 0) d = 0;
return d1+d2+d == dex ? compute_descendants(s1,d1) * compute_descendants(s2,d2) : 0;
}
else {
mycell *ss1 = s1->right, *ss2 = s2->right;
if(d > 0) {
int side = 0;
while(true) {
ss1->build(); ss1 = ss1->grightsibling(); if(ss1 == s2->left) { side=1; break; }
ss2->build(); ss2 = ss2->grightsibling(); if(ss2 == s1->left) { side=2; break; }
}
d += 100 * side;
}
allsegments.insert(segmentcode(s1));
allsegments.insert(segmentcode(s2));
auto memokey = make_tuple(segmentcode(s1), segmentcode(s2), d, d1, d2, dex);
static map<decltype(memokey), int> mem;
if(mem.count(memokey)) return mem[memokey];
int total = 0;
for(auto s3: all_child_segments(s1))
for(auto s4: all_child_segments(s2))
total += compute_in_dist(s3, s4, d1-1, d2-1, dex);
if(0) if(mem.count(memokey) && mem[memokey] != total) {
printf("%d vs %d :: %x %x d=%d %d,%d,%d\n", mem[memokey], total, segmentcode(s1), segmentcode(s2), d, d1, d2, dex);
return mem[memokey];
}
return mem[memokey] = total;
}
}
void do_analyze_dists(int rad) {
println(hlog, "do_analyze_dists (", rad, ")");
indenter_finish indf;
auto m = mroot;
auto seg = getsegment(m, m, 0, true);
// compute the correct answer, but not if this requires creating more than 1500 cells
celllister cl(croot(), rad, 1500, NULL);
vector<int> correct(2*rad+4, 0);
for(cell *c1: cl.lst) if(celldist(c1) == rad)
for(cell *c2: cl.lst) if(celldist(c2) == rad)
correct[celldistance(c1,c2)]++;
int total = 0;
for(int a=0; a<2*rad+4; a++) {
int cd = compute_in_dist(seg, seg, rad, rad, a);
printf("%2d: %d/%d\n", a, cd, correct[a]);
total += cd;
}
printf("total = %d (%d)\n", total, cgi.expansion->get_descendants(5).approx_int() * cgi.expansion->get_descendants(5).approx_int());
printf("all segments = %d\n", isize(allsegments));
// printf("descendants = %d (%d)\n", compute_descendants(seg, 5), int(.1+expansion.get_descendants(5).approx_int()));
}
}