The programs below count polyominoes with fourfold rotational symmetry that have inner rings of length 16 or more. One version has a ring centered on a vertex and the other centered on a face. The ring is passed to a subroutine that determines the possible outside and inside additions that can be made to it, and these are conjoined to obtain a full count. Rings can be classified as chiral and achiral. The achiral results appear in the first 16,24,... and 20,28,... lines below. The chiral results appear farther down. Separate programs are required to obtain results for ring sizes of 4, 8, and 12, none of which can have inside additions. Here are the calculations for n-ominoes with n divisible by four and inner-ring size r. r\n 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 4 1 2 5 15 49 162 551 1930 6875 24807 90556 333774 1239876 4636171 17435443 8 1 3 8 25 83 285 994 3529 12734 46469 171106 635074 2373269 8920590 12 1 4 12 39 133 463 1638 5882 21401 78709 291932 1090347 4096964 16,24,... 2 10 39 146 549 2050 7626 28441 106513 400386 1510159 5715076 20,28,... 2 13 58 234 921 3581 13763 52524 200082 762640 2910211 Total 1 3 9 29 98 336 1173 4170 15013 54630 200600 742626 2767350 10372586 39078284 A142886 1 1 3 5 12 20 45 80 173 310 664 1210 2570 4728 9976 Diff. 2 6 24 86 316 1128 4090 14840 54320 199936 741416 2764780 10367858 39068308 Half 1 3 12 43 158 564 2045 7420 27160 99968 370708 1382390 5183929 19534154 16,24,... 2 15 78 368 1663 7261 30927 129683 538263 2217287 20,28,... 1 5 24 112 495 2113 8867 36828 151628 619894 2521607 A144553 1 3 12 44 165 603 2235 8283 30936 116096 438463 1663701 6342086 24273048 For values of A144553 with n equivalent to one modulus four (which must include the center face), we run a separate program that counts polyominoes that have a child at (0,0) and may not include (1,0), which we call 1* below. The entries for 8(n-1) are the same as for 8 above. The results are r\n 1 5 9 13 17 21 25 29 33 37 41 45 49 53 57 61 1* 1 1 1 3 10 32 108 373 1314 4712 17119 62799 232344 866000 3247962 12246708 8(n-1) 1 3 8 25 83 285 994 3529 12734 46469 171106 635074 2373269 8920590 Sum->1 1 1 2 6 18 57 191 658 2308 8241 29853 109268 403450 1501074 5621231 21167298 A142886 1 1 2 2 4 7 11 20 36 65 117 216 396 736 1369 2558 Diff. 4 14 50 180 638 2272 8176 29736 109052 403054 1500338 5619862 21164740 A144553 2 7 25 90 319 1136 4088 14868 54526 201527 750169 2809931 10582370 There are three separate C++ program files below: main.cpp, Lattice.cpp, and Lattice.hpp. They are separated by a long comment line filled with asterisks. The programs are not finished products; they contain some #if sections for debugging purposes. The programs can be run in parallel by using separate values of acol for different runs or breaking down the inner loops used even further. The output provides results for achiral and chiral inner loops separately. In addition, these numbers are broken down by inner-loop size. The number of inner loops used is also shown. Timing is for my 2010 iMac with a 2.93 GHz processor. /****************************************************************************************/ // // main.cpp #include "Lattice.hpp" // this program calculates the number of polyominoes with fourfold rotational symmetry. If FACE_CENTER is // defined, it calculates those with an even number of cells in a ring quadrant, beginning with four. // Otherwise it does so for those with an odd number of cells in a ring quadrant, starting with five. int main(int argc, const char * argv[]) { for (int acol=MINCOL; acol= NROWR || col >= NCOLR) { // ring cell is too far out while (next_ring && (++ring[next_ring-1].next_direction_index > 2 || R_NONE == ring[next_ring-1].next_direction[ring[next_ring-1].next_direction_index])) delete_ring(); return; // we are ready to add_ring() again } // check distance to goal, (acol,0) int distance; switch (quadrant) { case 0: distance = col + abs(LASTROW - row); break; case 1: // left one quadrant distance = row + 1 + abs(col - acol); break; case -1: // right one quadrant distance = abs(ROWM) + LASTROW + col + 1; break; case 2: // left two quadrants case -2: // right two quadrants distance = row + col + LASTROW + 2; break; default: cout << "bad exit -2\n"; exit (-2); } if (1 == distance) { // if we are adjacent to last cell if (NMAX - 2 >= next_ring) { // if we have a complete ring #ifdef ERROR_CHECK if (quadrant) { cout << "bad error -3\n"; exit (-3); // we need to be in original quadrant } #endif if (col) { // if the cell is (LASTROW,1) ring[next_ring] = {char(LASTROW),1,0,char(prev_direction), {R_LEFT,R_NONE,R_NONE},0,{NULL,NULL}}; ring[next_ring+1] = {char(LASTROW),0,0,R_RIGHT,{R_LEFT,R_NONE,R_NONE},0,{NULL,NULL}}; } else if (LASTROW-1==row) { // if the cell is (acol,0) ring[next_ring] = {char(LASTROW-1),0,0,char(prev_direction), {R_UP,R_NONE,R_NONE},0,{NULL,NULL}}; ring[next_ring+1] = {char(LASTROW),0,0,R_DOWN,{R_LEFT,R_NONE,R_NONE},0,{NULL,NULL}}; } else { // if the cell is (LASTROW+1,0) ring[next_ring] = {char(LASTROW+1),0,0,char(prev_direction), {R_DOWN,R_NONE,R_NONE},0,{NULL,NULL}}; ring[next_ring+1] = {char(LASTROW),0,0,R_UP,{R_LEFT,R_NONE,R_NONE},0,{NULL,NULL}}; } // check for wrong chirality switch (ring_chirality()) { case 1: // chiral right process_ring(true); break; case 0: // achiral process_ring(false); break; case -1: break; // chiral left: wrong chirality } } while (next_ring && (++ring[next_ring-1].next_direction_index > 2 || R_NONE == ring[next_ring-1].next_direction[ring[next_ring-1].next_direction_index])) delete_ring(); return; } // 1 == distance if (distance < NMAX - next_ring) { // add another ring cell // insert border cells for ring[next_ring-1] int bi = 0; // index for border[] switch (ring[next_ring-1].prev_direction) { case R_LEFT: switch (ring[next_ring-1].next_direction[ring[next_ring-1].next_direction_index]) { case R_UP: // two outside if (ring[next_ring-1].col < NCOLR-1) { // if we can go more right RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col + 1; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].row) { // if we can go down RCell *bcell = lattice[ring[next_ring-1].row-1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go down; need a new quadrant RCell *bcell = lattice[ring[next_ring-1].COLP]; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; case R_RIGHT: // UP inside, DOWN outside if (ring[next_ring-1].row < NROWR-1) { // if we can go more up RCell *bcell = lattice[ring[next_ring-1].row + 1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].row) { // if we can go down RCell *bcell = lattice[ring[next_ring-1].row-1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go down; need a new quadrant RCell *bcell = lattice[ring[next_ring-1].COLP]; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; case R_DOWN: // two inside if (ring[next_ring-1].row < NROWR-1) { // if we can go more up RCell *bcell = lattice[ring[next_ring-1].row + 1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].col < NCOLR-1) { // if we can go more right RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col + 1; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; } break; case R_UP: switch (ring[next_ring-1].next_direction[ring[next_ring-1].next_direction_index]) { case R_RIGHT: // two outside if (ring[next_ring-1].row) { // if we can go down RCell *bcell = lattice[ring[next_ring-1].row-1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go down; need a new quadrant RCell *bcell = lattice[ring[next_ring-1].COLP]; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].col) { // if we can go left RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col - 1; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go left; need a new quadrant RCell *bcell = lattice[0] + ring[next_ring-1].ROWM; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; case R_DOWN: // RIGHT inside, LEFT outside if (ring[next_ring-1].col < NCOLR-1) { // if we can go more right RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col + 1; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].col) { // if we can go left RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col - 1; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go left; need a new quadrant RCell *bcell = lattice[0] + ring[next_ring-1].ROWM; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; case R_LEFT: // two inside if (ring[next_ring-1].col < NCOLR-1) { // if we can go more right RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col + 1; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].row) { // if we can go down RCell *bcell = lattice[ring[next_ring-1].row-1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go down; need a new quadrant RCell *bcell = lattice[ring[next_ring-1].COLP]; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; } break; case R_RIGHT: switch (ring[next_ring-1].next_direction[ring[next_ring-1].next_direction_index]) { case R_DOWN: // two outside if (ring[next_ring-1].col) { // if we can go left RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col - 1; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go left; need a new quadrant RCell *bcell = lattice[0] + ring[next_ring-1].ROWM; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].row < NROWR-1) { // if we can go more up RCell *bcell = lattice[ring[next_ring-1].row + 1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; case R_LEFT: // DOWN inside, UP outside if (ring[next_ring-1].row) { // if we can go down RCell *bcell = lattice[ring[next_ring-1].row-1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go down; need a new quadrant RCell *bcell = lattice[ring[next_ring-1].COLP]; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].row < NROWR-1) { // if we can go more up RCell *bcell = lattice[ring[next_ring-1].row + 1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; case R_UP: // two inside if (ring[next_ring-1].row) { // if we can go down RCell *bcell = lattice[ring[next_ring-1].row-1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go down; need a new quadrant RCell *bcell = lattice[ring[next_ring-1].COLP]; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].col) { // if we can go left RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col - 1; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go left; need a new quadrant RCell *bcell = lattice[0] + ring[next_ring-1].ROWM; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; } break; case R_DOWN: switch (ring[next_ring-1].next_direction[ring[next_ring-1].next_direction_index]) { case R_LEFT: // two outside if (ring[next_ring-1].row < NROWR-1) { // if we can go more up RCell *bcell = lattice[ring[next_ring-1].row + 1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].col < NCOLR-1) { // if we can go more right RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col + 1; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; case R_UP: // LEFT inside, RIGHT outside if (ring[next_ring-1].col) { // if we can go left RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col - 1; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go left; need a new quadrant RCell *bcell = lattice[0] + ring[next_ring-1].ROWM; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].col < NCOLR-1) { // if we can go more right RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col + 1; if (!*bcell) { *bcell = char(R_OUTSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; case R_RIGHT: // two inside if (ring[next_ring-1].col) { // if we can go left RCell *bcell = lattice[ring[next_ring-1].row] + ring[next_ring-1].col - 1; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } else { // we can't go left; need a new quadrant RCell *bcell = lattice[0] + ring[next_ring-1].ROWM; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } if (ring[next_ring-1].row < NROWR-1) { // if we can go more up RCell *bcell = lattice[ring[next_ring-1].row + 1] + ring[next_ring-1].col; if (!*bcell) { *bcell = char(R_INSIDE); ring[next_ring-1].border[bi++] = bcell; } } break; } break; } for (int i=bi; i<2; i++) ring[next_ring-1].border[i] = NULL; ring[next_ring] = {char(row),char(col),char(quadrant),char(prev_direction), {R_NONE,R_NONE,R_NONE},0,{NULL,NULL}}; // determine possible next cell directions int ndi = 0; // next direction index switch (ring[next_ring].prev_direction) { case R_LEFT: if (row acol) ring[next_ring].next_direction[ndi++] = R_DOWN; break; case R_UP: if (col acol) ring[next_ring].next_direction[ndi++] = R_DOWN; if (col || row > LASTROW) ring[next_ring].next_direction[ndi++] = R_LEFT; break; case R_RIGHT: if (row || col > acol) ring[next_ring].next_direction[ndi++] = R_DOWN; if (col || row > LASTROW) ring[next_ring].next_direction[ndi++] = R_LEFT; if (row LASTROW) ring[next_ring].next_direction[ndi++] = R_LEFT; if (row 2 || R_NONE == ring[next_ring-1].next_direction[ring[next_ring-1].next_direction_index])) delete_ring(); return; } void RLattice::delete_ring() { if (!--next_ring) return; // we are done lattice[ring[next_ring].row][ring[next_ring].col] = R_EMPTY; for (int i=0; i<2; i++) { if (ring[next_ring-1].border[i]) *ring[next_ring-1].border[i] = R_EMPTY; else break; } ++ring[next_ring].next_direction_index; } int RLattice::ring_chirality() { // -1 if left, 0 if achiral, 1 if right if (R_LEFT == ring[0].next_direction[ring[0].next_direction_index]) return 1; #ifdef FACE_CENTER for (int i=1; iadd_ring_children(); if (lattice->any_child_in()) lattice->redel_in(); lattice->redel_out(); lattice->count_poly(); // how does one delete lattice? delete lattice; //print_ring(true); } void RLattice::print_ring(bool chiral) { #if 0 bool quad_shift = false; for (int i=0; i0) { quad_shift = true; break; } } if (!quad_shift) return; #endif if (chiral) cout << "C "; else cout << "A "; cout << "(0," << acol << ")"; for (int i=1; i=FIRST_RING && ring_index 0 for (add_parent_in(); ; add_parent_in()) { // also count polyomino if (limit_reached_in()) delete_parent_in(); for (++next_child_in; children_done_in(); next_child_in++) { if (parents_done_in()) return; delete_parent_in(); } } } void Lattice::redel_out() { // requires child_in_count > 0 for (add_parent_out(); ; add_parent_out()) { // also count polyomino if (limit_reached_out()) delete_parent_out(); for (++next_child_out; children_done_out(); next_child_out++) { if (parents_done_out()) return; delete_parent_out(); } } } void Lattice::add_parent_in() { // we either add a new parent based on the current next_child_in along with // its children in any adjacent empty squares or we return. If we add the // parent, we increase next_parent_in and we zero the parent_successor // fields for parent_predecessor, if any; these need not be adjacent cells, but // they must be connected to an adjacent cell. If parent added, count polyomino. parent_in[next_parent_in] = {(unsigned char)next_child_in, (unsigned char)child_in_n, child_in[next_child_in].low_ring, child_in[next_child_in].high_ring}; // temporary (assume zeros at end!) int row = child_in[next_child_in].row; // coordinates of new parent cell int col = child_in[next_child_in].col; // check for conflict in ring attachments int cell_num = lattice[row+1][col]; // cell above if (bad_connection(cell_num, 0)) return; cell_num = lattice[row][col+1]; // cell at right if (bad_connection(cell_num, 0)) return; int quad_adjust; // expect new cell to have this added to ring contact if (row) { cell_num = lattice[row-1][col]; // cell below quad_adjust = 0; } else { cell_num = lattice[COLP][0]; quad_adjust = ring_cell_n; } if (bad_connection(cell_num, quad_adjust)) return; #ifdef FACE_CENTER if (row || col) { // unused center cell left of lattice[0][0] for even rings #endif if (col) { cell_num = lattice[row][col-1]; // cell at left quad_adjust = 0; } else { cell_num = lattice[0][ROWM]; quad_adjust = -ring_cell_n; } if (bad_connection(cell_num, quad_adjust)) return; #ifdef FACE_CENTER } #endif // we have a new parent, as we did not return because of a conflict for (int i=0; parent_in[next_parent_in].parent_predecessor[i]; i++) { // for each adjacent parent cell parent_in[parent_in[next_parent_in].parent_predecessor[i]-1].parent_successor = next_parent_in; // link } child_in[parent_in[next_parent_in].child_index].parent_index = next_parent_in+1; // link from child to parent // add neighbors as children if empty if (EMPTY == lattice[row+1][col]) { lattice[row+1][col] = child_in_n + ring_cell_n + FIRST_RING; child_in[child_in_n++] = {(RowCol)(row+1), (RowCol)col, parent_in[next_parent_in].low_ring, parent_in[next_parent_in].high_ring, DOWN}; } if (EMPTY == lattice[row][col+1]) { lattice[row][col+1] = child_in_n + ring_cell_n + FIRST_RING; child_in[child_in_n++] = {(RowCol)row, (RowCol)(col+1), parent_in[next_parent_in].low_ring, parent_in[next_parent_in].high_ring, LEFT}; } if (row) { if (EMPTY == lattice[row-1][col]) { lattice[row-1][col] = child_in_n + ring_cell_n + FIRST_RING; child_in[child_in_n++] = {(RowCol)(row-1), (RowCol)col, parent_in[next_parent_in].low_ring, parent_in[next_parent_in].high_ring, UP}; } } else { if (EMPTY == lattice[COLP][0]) { lattice[COLP][0] = child_in_n + ring_cell_n + FIRST_RING; child_in[child_in_n++] = {RowCol(COLP), 0, char(parent_in[next_parent_in].low_ring + ring_cell_n), char(parent_in[next_parent_in].high_ring + ring_cell_n), LEFT}; } } if (col) { if (EMPTY == lattice[row][col-1]) { lattice[row][col-1] = child_in_n + ring_cell_n + FIRST_RING; child_in[child_in_n++] = {(RowCol)row, (RowCol)(col-1), parent_in[next_parent_in].low_ring, parent_in[next_parent_in].high_ring, RIGHT}; } #ifdef FACE_CENTER } else if (row) { // unused center cell left of lattice[0][0] for even rings #else } else { #endif if (EMPTY == lattice[0][ROWM]) { lattice[0][ROWM] = child_in_n + ring_cell_n + FIRST_RING; child_in[child_in_n++] = {0, RowCol(ROWM), char(parent_in[next_parent_in].low_ring - ring_cell_n), char(parent_in[next_parent_in].high_ring - ring_cell_n), DOWN}; } } child_in[next_child_in].parent_index = ++next_parent_in; // actual+1 #if 0 if (!ring_is_chiral) { // if ring is achiral; face center CPairSet lower, upper; for (int i=0; icol+1) upper.insert(CPair(col+1,row-1)); else if (rowcol) upper.insert(CPair(col,row)); } if (upper!=lower) return; // do not count chiral } #endif #if 0 if (!ring_is_chiral) { // if ring is achiral CPairSet lower, upper, middle; for (int i=0; icol) upper.insert(CPair(col,row)); else middle.insert(CPair(row,col)); } if (upper!=lower) return; // do not count achiral for (auto cpx: middle) lower.insert(cpx); // join middle and lower cpairset2[next_parent_in][0].insert(lower); } #endif pcount[0][next_parent_in]++; //if (!ring_is_chiral && 0==achiral_ring_count && 1==next_parent_in) dump_ring(); #if 0 static int count52x = 0; if (97==count52 && 8==next_parent_in) { cout << ++count52x << ":"; for (int i=0; icol+1) upper.insert(CPair(col+1,row-1)); else if (rowcol) upper.insert(CPair(col,row)); } if (upper!=lower) return; // do not count chiral } #endif #if 0 if (!ring_is_chiral) { // if ring is achiral CPairSet lower, upper, middle; for (int i=0; icol) upper.insert(CPair(col,row)); else middle.insert(CPair(row,col)); } if (upper!=lower) return; // do not count chiral for (auto cpx: middle) lower.insert(cpx); // join middle and lower cpairset2[next_parent_out][1].insert(lower); } #endif pcount[1][next_parent_out]++; //dump_ring(); } void Lattice::delete_parent_in() { next_child_in = parent_in[--next_parent_in].child_index; while (child_in_n != parent_in[next_parent_in].child_begin) { // eliminate neighbors child_in_n--; lattice[child_in[child_in_n].row][child_in[child_in_n].col] = EMPTY; } for (int i=0; parent_in[next_parent_in].parent_predecessor[i]; i++) { // for each adjacent parent cell parent_in[parent_in[next_parent_in].parent_predecessor[i]-1].parent_successor = 0; // delete link } child_in[parent_in[next_parent_in].child_index].parent_index = 0; // eliminate child link } void Lattice::delete_parent_out() { next_child_out = parent_out[--next_parent_out].child_index; while (child_out_n != parent_out[next_parent_out].child_begin) { // eliminate neighbors child_out_n--; lattice[child_out[child_out_n].row][child_out[child_out_n].col] = EMPTY; } } bool Lattice::bad_connection( // return true if this is a bad connection int cell_num, // content of adjacent lattice cell int quad_adjust // expect adjacent cell to have this added to ring contact // generally add quad_adjust to parent_in[next_parent_in].---ring ) { if (cell_num < ring_cell_n+FIRST_RING) return false; // neighbor is not inside child if (!child_in[cell_num-ring_cell_n-FIRST_RING].parent_index) return false; // neighbor not a parent cell if (parent_in[next_parent_in].low_ring != parent_in[next_parent_in].high_ring) { // if two ring connections for new parent cell #ifdef ERROR_CHECK if (1 != parent_in[next_parent_in].high_ring - parent_in[next_parent_in].low_ring) { cout << "wrong ring refs\n"; exit (-4); } #endif if (child_in[cell_num-ring_cell_n-FIRST_RING].low_ring < parent_in[next_parent_in].low_ring + quad_adjust) return true; if (child_in[cell_num-ring_cell_n-FIRST_RING].high_ring > parent_in[next_parent_in].high_ring + quad_adjust) return true; // determine parent successor int successor = child_in[cell_num-ring_cell_n-FIRST_RING].parent_index-1; #ifdef ERROR_CHECK if (successor<0) { cout << "bad successor number\n"; exit(-11); } #endif while (parent_in[successor].parent_successor) successor = parent_in[successor].parent_successor; if (parent_in[successor].low_ring == parent_in[successor].high_ring) { for (int i=0; i<4; i++) if (!parent_in[next_parent_in].parent_predecessor[i]) { parent_in[next_parent_in].parent_predecessor[i] = successor+1; return false; } } else { // successor has two ring connections // determine quadrant adjustment for successor if (parent_in[successor].low_ring < parent_in[next_parent_in].low_ring) { int successor_adjust = ring_cell_n; while (parent_in[successor].low_ring + successor_adjust < parent_in[next_parent_in].low_ring) successor_adjust += ring_cell_n; if (parent_in[successor].low_ring + successor_adjust != parent_in[next_parent_in].low_ring) return true; if (parent_in[successor].high_ring + successor_adjust != parent_in[next_parent_in].high_ring) return true; } else if (parent_in[successor].low_ring > parent_in[next_parent_in].low_ring) { int successor_adjust = ring_cell_n; while (parent_in[successor].low_ring - successor_adjust > parent_in[next_parent_in].low_ring) successor_adjust += ring_cell_n; if (parent_in[successor].low_ring - successor_adjust != parent_in[next_parent_in].low_ring) return true; if (parent_in[successor].high_ring - successor_adjust != parent_in[next_parent_in].high_ring) return true; } else { // no adjustment required if (parent_in[successor].high_ring != parent_in[next_parent_in].high_ring) return true; } } } else { // only one ring connection for trial parent // first check if adjacent cell has one or two attachments int successor = child_in[cell_num-ring_cell_n-FIRST_RING].parent_index-1; int adj_parent = successor; // index of adjacent parent, which may have successors while (parent_in[successor].parent_successor) successor = parent_in[successor].parent_successor; int succ_quad_adjust; // adjustment for change in quad from adj_parent to successor if (adj_parent == successor) succ_quad_adjust = 0; else { // ***** change on 5/22 ***** succ_quad_adjust = ring_cell_n * ((2+abs(parent_in[successor].low_ring - parent_in[adj_parent].low_ring))/ring_cell_n); // could be zero if (parent_in[successor].high_ring < parent_in[adj_parent].low_ring) succ_quad_adjust = -succ_quad_adjust; } // return true if proposed parent would add new connection if (parent_in[successor].low_ring != parent_in[successor].high_ring) { if (parent_in[successor].low_ring - quad_adjust - succ_quad_adjust > parent_in[next_parent_in].low_ring) return true; if (parent_in[successor].high_ring - quad_adjust - succ_quad_adjust < parent_in[next_parent_in].high_ring) return true; // expand connections of trial parent if (parent_in[successor].low_ring - quad_adjust - succ_quad_adjust == parent_in[next_parent_in].low_ring) { parent_in[next_parent_in].high_ring = parent_in[next_parent_in].low_ring + 1; } else parent_in[next_parent_in].low_ring = parent_in[next_parent_in].high_ring - 1; } else { // check if possible connection where adjacent has one attachment if (abs(parent_in[successor].low_ring - quad_adjust - succ_quad_adjust - parent_in[next_parent_in].low_ring) > 1) return true; // find double connection if any //int adjacent_parent = successor; //while (parent_in[successor].parent_successor) //successor = parent_in[successor].parent_successor; if (parent_in[successor].low_ring != parent_in[successor].high_ring) { if (!((parent_in[successor].low_ring-parent_in[next_parent_in].low_ring) % ring_cell_n)) parent_in[next_parent_in].high_ring++; else if (!((parent_in[successor].high_ring-parent_in[next_parent_in].high_ring) % ring_cell_n)) parent_in[next_parent_in].low_ring--; else return true; // adjacent parent would extend to three } else { // only one connection for adjacent cell if (parent_in[successor].low_ring - quad_adjust - succ_quad_adjust > parent_in[next_parent_in].low_ring) { parent_in[next_parent_in].high_ring = parent_in[successor].low_ring - quad_adjust - succ_quad_adjust; #ifdef ERROR_CHECK if (1 != parent_in[next_parent_in].high_ring - parent_in[next_parent_in].low_ring) { cout << "wrong ring refs\n"; exit (-4); } #endif } else if (parent_in[successor].low_ring - quad_adjust - succ_quad_adjust < parent_in[next_parent_in].low_ring) { parent_in[next_parent_in].low_ring = parent_in[successor].low_ring - quad_adjust - succ_quad_adjust; #ifdef ERROR_CHECK if (1 != parent_in[next_parent_in].high_ring - parent_in[next_parent_in].low_ring) { cout << "wrong ring refs\n"; exit (-4); } #endif } for (int i=0; i<4; i++) { // trial parent is potential successor if (!parent_in[next_parent_in].parent_predecessor[i]) { parent_in[next_parent_in].parent_predecessor[i] = successor + 1; break; } } } } } return false; } void Lattice::count_poly() { #if 0 if (!ring_is_chiral) { achiral_ring_count++; for (int i=1; i<=NMAX-ring_cell_n; i++) { if (1==pcount[0][i]%2) { cout << achiral_ring_count << " has inside odd value at " << i << endl; exit (-7); } if (1==pcount[1][i]%2) { cout << achiral_ring_count << " has outside odd value at " << i << endl; exit (-7); } } } #endif Int total[NMAX] = {0}; for (int i=0; i<=NMAX-ring_cell_n; i++) { for (int j=0; j<=NMAX-ring_cell_n-i; j++) { total[ring_cell_n+i+j-4] += pcount[0][i] * pcount[1][j]; } } for (int i=0; i=0; ri-=1) { // for each row for (int ci=0; ci=FIRST_RING+ring_cell_n) { int successor = child_in[index-FIRST_RING-ring_cell_n].parent_index; if (successor--) { while (parent_in[successor].parent_successor) successor = parent_in[successor].parent_successor; if (parent_in[successor].low_ring == parent_in[successor].high_ring) cout << 'X'; else cout << 'Z'; } else cout << 'i'; // child (i) } else if (index>=FIRST_RING) cout << char('A'-FIRST_RING+index); else switch (index) { case EMPTY: cout << ' '; break; case FORBIDDEN: cout << '-'; break; case OUTSIDE: // outside child cout << 'o'; break; case PARENT: // outside child cout << 'Y'; break; default: cout << "error in case\n"; exit (-2); // error } } cout << endl; } cout << "in=" << child_in_n << " out=" << child_out_n << endl; for (int i=0; i using std::cout; using std::endl; #include using std::set; using std::pair; // following slows down code with error checking //#define ERROR_CHECK // do not define FACE_CENTER if center is a vertex #define FACE_CENTER #ifdef FACE_CENTER #define NMAX 22 // maximum number of cells in quarter of polyomino; must be even // 16 takes 2.7 seconds; 18 takes 33.1 secs; 20 takes 479.1 secs; 22 takes 6749 secs. #define NROWR NMAX/2 + 2 #define NCOLR NMAX/2 + 1 #define MINCOL 1 #define LASTROW acol+1 #define ROWM row-1 #define COLP col+1 #define NROW NMAX #define NCOL NMAX-1 #else #define NMAX 23 // maximum number of cells in quarter of polyomino; must be odd // 19 takes 73.9 seconds; 21 takes 1084 secs; 23 takes 16093.3 secs. #define NROWR (NMAX+1)/2+1 #define NCOLR (NMAX+1)/2+1 #define MINCOL 2 #define LASTROW acol #define ROWM row #define COLP col #define NROW NMAX-1 #define NCOL NMAX-1 #endif typedef unsigned long long int Int; enum {R_EMPTY=0, R_INSIDE=-1, R_OUTSIDE=-2, R_FORBIDDEN=-3}; // values for RCell typedef char RCell; // nonzero if in use; ring cell is positive integer enum {R_NONE, R_LEFT, R_UP, R_RIGHT, R_DOWN}; // directions typedef struct { char row; char col; char quadrant; // 0 for first, 1 for second, -1 for fourth char prev_direction; // see directions above char next_direction[3]; // possible directions of next ring cell char next_direction_index; // index to actual direction above RCell *border[2]; // border cells of this ring cell, possibly null } RingCellR; class RLattice { // used to construct all of the possible rings, >=4 and <= NMAX RCell lattice[NROWR][NCOLR]; // {4,4} RingCellR ring[NMAX]; int acol; // lowest column for first cell of ring int next_ring; // ring[next_ring] is next ring cell to be added void add_ring(); void delete_ring(); int ring_chirality(); void process_ring(bool chiral); void print_ring(bool chiral); friend class Lattice; public: RLattice(int acol_in); void get_rings() { while (next_ring) add_ring(); }; // count rings }; typedef unsigned char Cell; // values below enum {EMPTY, FORBIDDEN, OUTSIDE, PARENT, FIRST_RING}; // FIRST_RING..FIRST_RING+ring_cell_n-1 are ring cells, higher numbers are inside children typedef unsigned char RowCol; typedef pair CPair; typedef set CPairSet; typedef set CPairSet2; enum {NONE, UP, RIGHT, DOWN, LEFT}; // clockwise typedef struct { RowCol row; RowCol col; unsigned char prev:4; // direction of previous cell unsigned char next:4; // direction of next cell char quad; // quadrant of rotation (0 for quadrant shown) } RingCell; // used in list of ring cells typedef struct { RowCol row; RowCol col; char low_ring; // index of associated ring cell, adjusted for rotation char high_ring; // index of associated ring cell, adjusted for rotation unsigned char dir; // direction of cell for which this is a child unsigned char parent_index; // 1 + index of parent this child becomes, else zero } ChildIn; typedef struct { unsigned char child_index; // index of child_in[] unsigned char child_begin; // where new children of this parent begin in child_in char low_ring; // index of associated ring cell, adjusted for rotation char high_ring; // index of associated ring cell (could be zero!) unsigned char parent_predecessor[4]; // 1+index of predecessor in parent_in[] unsigned char parent_successor; // index origin 0 (parent_in[0] cannot be successor) } ParentIn; typedef struct { RowCol row; RowCol col; } ChildOut; typedef struct { char child_index; // offset to child that has become this parent char child_begin; // offset of first child of this parent } ParentOut; class Lattice { // used to enumerate inside and outside ring Cell lattice[NROW][NCOL]; // {4,4} RingCell ring_cell[NMAX]; // only fourth of ring here bool ring_is_chiral; // true if ring is chiral, false if achiral int ring_cell_n; // number of ring cells in ring_cell[] ChildIn child_in[3*NMAX]; int child_in_n; // number of cells in child_in ParentIn parent_in[NMAX]; ParentOut parent_out[NMAX]; ChildOut child_out[3*NMAX]; int child_out_n; // number of cells in child_out int next_child_in; int next_child_out; int next_parent_in; int next_parent_out; Int pcount[2][NMAX]; // child_in, child_out void add_child_in(RowCol, RowCol, unsigned char, signed char, unsigned char); void add_child_out(RowCol, RowCol); bool is_ring_cell(int row, int col); void add_parent_in(); void add_parent_out(); void delete_parent_in(); void delete_parent_out(); bool children_done_in() { return next_child_in == child_in_n; } bool children_done_out() { return next_child_out == child_out_n; } bool parents_done_in() { return next_parent_in == 0; } bool parents_done_out() { return next_parent_out == 0; } bool limit_reached_in() { return ring_cell_n + next_parent_in >= NMAX; } bool limit_reached_out() { return ring_cell_n + next_parent_out >= NMAX; } bool bad_connection(int cell_num, int quad); public: Lattice(class RLattice &rlat, bool chiral) : child_in_n(0), child_out_n(0), next_child_in(0), next_child_out(0), next_parent_in(0), next_parent_out(0) { memset(lattice, 0, sizeof lattice); ring_is_chiral = chiral; memset(pcount, 0, sizeof pcount); pcount[0][0] = pcount[1][0] = 1; // no child_in, no child_out ring_cell_n = rlat.next_ring+2; for (int i=0; i0;} const void dump_ring(); }; void print_ring_count(); void print_poly_count(); #endif /* Lattice_hpp */ /****************************************************************************************/