The program below counts polyominoes with fourfold rotational symmetry that have inner rings of length 16 or more with the ring 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. Results appear in the rows labeled 16, 24, ....
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 200630 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 199966 741416 2764780 10367858 39068308
     Half   1  3 12 43 158  564 2045  7420 27160  99983 370708 1382390  5183929 19534154
16,24,...                2   15   78   368  1663   7261  30927  129685   538288  2217470
20,28,...            1   5   24  112   495  2113   8867  36830  151645   619994  2522143
  A144553   1  3 12 44 165  603 2235  8283 30936 116111 438465 1663720	6342211 24273767

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. 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
//  ring_4c2_16p
//
//  Created by Robert A Russell on 10/16/21.
//  Copyright © 2021 Robert A Russell. All rights reserved.
//

#include "Lattice.hpp"

// this program calculates the number of polyominoes with fourfold rotational symmetry and an even
// number of cells in a ring quadrant, beginning with four.  Each ring is cnetered on the center
// of a tile.

int main(int argc, const char * argv[]) {
	for (int acol=MINCOL; acol<NCOLR-1; acol++) {
		cout << "acol=" << acol << endl;
		RLattice lattice(acol);
		lattice.get_rings();
	}
	//cout << endl;
	print_poly_count();
	print_ring_count();
	
	return 0;
}
/****************************************************************************************/
//
//  Lattice.cpp
//  ring_4c2_16p
//
//  Created by Robert A Russell on 10/16/21.
//  Copyright © 2021 Robert A Russell. All rights reserved.
//

#include "Lattice.hpp"

static Int ring_count[NMAX][2] = {0};
static Int poly_count[NMAX][NMAX][2] = {0}; // [ring size - 4][poly_size - 4][chiral]

RLattice::RLattice(int acol_in) {
	acol = acol_in;
	for (int i=0; i<NROWR; i++) for (int j=0; j<NCOLR; j++) lattice[i][j] = R_EMPTY;
	for (int i=0; i<acol; i++) lattice[0][i] = R_FORBIDDEN;
	count[0] = 0;
	count[1] = 0;
	ring[0] = {0,short(acol),0,R_DOWN,{R_UP,R_RIGHT,NONE},0,{NULL,NULL}};
	lattice[0][acol] = 1;
	next_ring = 1;
}

void RLattice::add_ring() {
	// we add ring[next_ring] and then increase next_ring until we reach RINGQ
	// then we determine if ring is achiral or chiral with correct sense
	// then we repeat delete_ring until we have a new opportunity to do add_ring
	
	// determine coordinates of next cell
	int row=ring[next_ring-1].row,
	col=ring[next_ring-1].col,
	quadrant = ring[next_ring-1].quadrant,
	prev_direction=R_NONE;
	
	switch (ring[next_ring-1].next_direction[ring[next_ring-1].next_direction_index]) {
		case R_LEFT:
			if (col) { // if column is not zero
				col--;
				prev_direction = R_RIGHT;
			} else { // move to higher quadrant
				col = ROWM;
				row = 0;
				quadrant++;
				prev_direction = R_DOWN;
			}
			break;
		case R_UP:
			row++;
			prev_direction = R_DOWN;
			break;
		case R_RIGHT:
			col++;
			prev_direction = R_LEFT;
			break;
		case R_DOWN:
			if (ring[next_ring-1].row) { // if row is not zero)
				row--;
				prev_direction = R_UP;
			} else { // move to lower quadrant
				row = COLP;
				col = 0;
				quadrant--;
				prev_direction = R_LEFT;
			}
			break;
	}
	// can we use this new ring cell?
	if (lattice[row][col] // new ring cell is occupied
		 || row >= NROWR || col >= NCOLR  // ring cell is too far out
		 || (quadrant && ((!col && (acol==row || acol+2==row)) || (1==col && acol+1==row))) // touch last
	) {
		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: // goal is (acol+1,0)
			distance = col + abs(LASTROW - row);
			break;
		case 1: // left one quadrant (r,c)->(c+1,-1-r)
			distance = row + 1 + abs(col - acol);
			break;
		case -1: // ight one quadrant (r,c)->(-col-1,r-1)
			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
			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<NROWR-1) ring[next_ring].next_direction[ndi++] = R_UP;
				if (col<NCOLR-1) ring[next_ring].next_direction[ndi++] = R_RIGHT;
				if (row || col > acol) ring[next_ring].next_direction[ndi++] = R_DOWN;
				break;
			case R_UP:
				if (col<NCOLR-1) ring[next_ring].next_direction[ndi++] = 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;
				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<NROWR-1) ring[next_ring].next_direction[ndi++] = R_UP;
				break;
			case R_DOWN:
				if (col || row > LASTROW) ring[next_ring].next_direction[ndi++] = R_LEFT;
				if (row<NROWR-1) ring[next_ring].next_direction[ndi++] = R_UP;
				if (col<NCOLR-1) ring[next_ring].next_direction[ndi++] = R_RIGHT;
				break;
		}
		for (int i=ndi; i<3; i++) ring[next_ring].next_direction[i] = R_NONE;
		lattice[ring[next_ring-1].row][ring[next_ring-1].col] = next_ring;
		next_ring++;
		return; // add another ring cell
	} // distance < NMAX - next_ring
	// we have too far to go; must try a different route
	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;
}

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_RIGHT == ring[0].next_direction[ring[0].next_direction_index]) return 1;
	for (int i=1; i<next_ring/2; i++) {
		int low_turn=0, high_turn=0;
		switch (ring[i].prev_direction) {
		case R_LEFT:
			switch (ring[i].next_direction[ring[i].next_direction_index]) {
				case R_UP: low_turn = 1; break;
				case R_RIGHT: low_turn = 2; break;
				case R_DOWN: low_turn = 3; break;
			}
			break;
		case R_UP:
			switch (ring[i].next_direction[ring[i].next_direction_index]) {
				case R_RIGHT: low_turn = 1; break;
				case R_DOWN: low_turn = 2; break;
				case R_LEFT: low_turn = 3; break;
			}
			break;
		case R_RIGHT:
			switch (ring[i].next_direction[ring[i].next_direction_index]) {
				case R_DOWN: low_turn = 1; break;
				case R_LEFT: low_turn = 2; break;
				case R_UP: low_turn = 3; break;
			}
			break;
		case R_DOWN:
			switch (ring[i].next_direction[ring[i].next_direction_index]) {
				case R_LEFT: low_turn = 1; break;
				case R_UP: low_turn = 2; break;
				case R_RIGHT: low_turn = 3; break;
			}
			break;
		}
		int ringi = next_ring+2-i;
		switch (ring[next_ring+2-i].next_direction[ring[next_ring+2-i].next_direction_index]) {
		case R_LEFT:
			switch (ring[ringi].prev_direction) {
				case R_DOWN: high_turn = 1; break;
				case R_RIGHT: high_turn = 2; break;
				case R_UP: high_turn = 3; break;
			}
			break;
		case R_UP:
			switch (ring[ringi].prev_direction) {
				case R_LEFT: high_turn = 1; break;
				case R_DOWN: high_turn = 2; break;
				case R_RIGHT: high_turn = 3; break;
			}
			break;
		case R_RIGHT:
			switch (ring[ringi].prev_direction) {
				case R_UP: high_turn = 1; break;
				case R_LEFT: high_turn = 2; break;
				case R_DOWN: high_turn = 3; break;
			}
			break;
		case R_DOWN:
			switch (ring[ringi].prev_direction) {
				case R_RIGHT: high_turn = 1; break;
				case R_UP: high_turn = 2; break;
				case R_LEFT: high_turn = 3; break;
			}
			break;
		}
		if (low_turn > high_turn) return 1;
		if (high_turn > low_turn) return -1;
	}
	return 0;
}
		
void print_ring_count() {
	cout << "rings of {4,4} n-ominoes with fourfold rotational symmetry\n";
	cout << "n chiral achiral\n";
	for (int i=0; i<NMAX; i++) {
		if (ring_count[i][0]) {
			cout << 4*(i+1) << " " << ring_count[i][1] << " " << ring_count[i][0] << endl;
		}
	}
	clock_t elapsed = clock();
	cout << (elapsed / (double)CLOCKS_PER_SEC) << " secs\n";
}
		
void print_poly_count() {
	for (int i=0; i<NMAX-3; i++) {
		cout << 4*(i+4) << ": [";
		Int total_chiral=0, total_achiral=0;
		for (int j=0; j<NMAX-3; j++) {
			total_achiral += poly_count[j][i][0];
			total_chiral += poly_count[j][i][1];
		}
		cout << total_achiral << "," << total_chiral << "]";
		for (int j=0; j<NMAX-3; j++) {
			cout << " (" << 4*(j+4) << ")";
			cout << "[" << poly_count[j][i][0] << "," << poly_count[j][i][1] << "]";
		}
		cout << endl;
	}
}
		
void RLattice::process_ring(bool chiral) {
	//if (11!=next_ring) return; // DEBUG - only process rings of 36
	ring_count[next_ring+1][chiral]++;
	Lattice *lattice = new Lattice(*this, chiral);
	lattice->add_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 (chiral) cout << "C ";
	else cout << "A ";
	cout << "(0," << acol << ")";
	for (int i=1; i<next_ring+2; i++) cout << ", (" << ring[i].row << "," << ring[i].col << ")";
	cout << endl;
}

bool Lattice::is_ring_cell(
	int row,
	int col
) {
#ifdef ERROR_CHECK
	if (col<0) {
		cout << "bad column\n";
		exit(-12);
	}
#endif
	unsigned int ring_index = lattice[row][col];
	return ring_index>=FIRST_RING && ring_index<FIRST_RING+ring_cell_n;
}

void Lattice::add_ring_children() { // only children of ring cells
	for (int i=0; i<ring_cell_n; i++) { // for each ring cell
		// determine turn at this cell: 1=right, 2=straight, 3=left
		int turn = (4 + ring_cell[i].prev - ring_cell[i].next) % 4;
		switch (ring_cell[i].prev) {
			case UP: // previous ring cell cell above
				switch (turn) {
					case 1: // right; two inside
						add_child_in(ring_cell[i].row, ring_cell[i].col+1,
							i, ring_cell[i].quad, LEFT);
						if (ring_cell[i].row)  add_child_in(ring_cell[i].row-1, ring_cell[i].col,
							i, ring_cell[i].quad, UP);
						else add_child_in(ring_cell[i].COLP, 0,
							i, ring_cell[i].quad-1, LEFT);
						break;
					case 2: // straight; one inside, one outside
						add_child_in(ring_cell[i].row, ring_cell[i].col+1,
							i, ring_cell[i].quad, LEFT);
						if (ring_cell[i].col) add_child_out(ring_cell[i].row, ring_cell[i].col-1);
						else add_child_out(0, ring_cell[i].ROWM);
						break;
					case 3: // left; two outside
						if (ring_cell[i].row) add_child_out(ring_cell[i].row-1, ring_cell[i].col);
						else add_child_out(ring_cell[i].COLP, 0);
						if (ring_cell[i].col) add_child_out(ring_cell[i].row, ring_cell[i].col-1);
						else add_child_out(0, ring_cell[i].ROWM);
						break;
					default:
						break;
				}
				break;
			case RIGHT: // previous ring cell cell right
				switch (turn) {
					case 1: // right; two inside
						if (ring_cell[i].row)  add_child_in(ring_cell[i].row-1, ring_cell[i].col,
							i, ring_cell[i].quad, UP);
						else add_child_in(ring_cell[i].COLP, 0,
							i, ring_cell[i].quad-1, LEFT);
						if (ring_cell[i].col) add_child_in(ring_cell[i].row, ring_cell[i].col-1,
							i, ring_cell[i].quad, RIGHT);
						else add_child_in(0, ring_cell[i].ROWM,
							i, ring_cell[i].quad+1, DOWN);
						break;
					case 2: // straight; one inside, one outside
						if (ring_cell[i].row)  add_child_in(ring_cell[i].row-1, ring_cell[i].col,
							i, ring_cell[i].quad, UP);
						else add_child_in(ring_cell[i].COLP, 0,
							i, ring_cell[i].quad-1, LEFT);
						add_child_out(ring_cell[i].row+1, ring_cell[i].col);
						break;
					case 3: // left; two outside
						if (ring_cell[i].col) add_child_out(ring_cell[i].row, ring_cell[i].col-1);
						else add_child_out(0,ring_cell[i].ROWM);
						add_child_out(ring_cell[i].row+1, ring_cell[i].col);
						break;
					default:
						break;
				}
				break;
			case DOWN: // previous ring cell cell below
				switch (turn) {
					case 1: // right; two inside
						if (ring_cell[i].col) add_child_in(ring_cell[i].row, ring_cell[i].col-1,
							i, ring_cell[i].quad, RIGHT);
						else add_child_in(0, ring_cell[i].ROWM,
							i, ring_cell[i].quad+1, DOWN);
						add_child_in(ring_cell[i].row+1, ring_cell[i].col,
							i, ring_cell[i].quad, DOWN);
						break;
					case 2: // straight; one inside, one outside
						if (ring_cell[i].col) add_child_in(ring_cell[i].row, ring_cell[i].col-1,
							i, ring_cell[i].quad, RIGHT);
						else add_child_in(0, ring_cell[i].ROWM,
							i, ring_cell[i].quad+1, DOWN);
						add_child_out(ring_cell[i].row, ring_cell[i].col+1);
						break;
					case 3: // left; two outside
						add_child_out(ring_cell[i].row+1, ring_cell[i].col);
						add_child_out(ring_cell[i].row, ring_cell[i].col+1);
						break;
					default:
						break;
				}
				break;
			case LEFT: // previous ring cell cell left
				switch (turn) {
					case 1: // right; two inside
						add_child_in(ring_cell[i].row, ring_cell[i].col+1,
							i, ring_cell[i].quad, LEFT);
						add_child_in(ring_cell[i].row+1, ring_cell[i].col,
							i, ring_cell[i].quad, DOWN);
						break;
					case 2: // straight; one inside, one outside
						add_child_in(ring_cell[i].row+1, ring_cell[i].col,
							i, ring_cell[i].quad, DOWN);
						if (ring_cell[i].row) add_child_out(ring_cell[i].row-1, ring_cell[i].col);
						else add_child_out(ring_cell[i].COLP, 0);
						break;
					case 3: // left; two outside
						if (ring_cell[i].row) add_child_out(ring_cell[i].row-1, ring_cell[i].col);
						else add_child_out(ring_cell[i].COLP, 0);
						add_child_out(ring_cell[i].row, ring_cell[i].col+1);
						break;
					default:
						break;
				}
				break;
			default:
				exit (-3);
				break;
		}
	}
}
		
void Lattice::add_child_in( // child must be adjacent to a ring cell
	RowCol row,
	RowCol col,
	unsigned char ring_index, // index of ring cell
	signed char quad, // quadrant of ring cell
	unsigned char dir // direction of ring cell
) {
	if (lattice[row][col]) return; // if already encountered
	if ((UP!=dir && is_ring_cell(row+1,col))
		|| (RIGHT!=dir && is_ring_cell(row,col+1))
		|| (DOWN!=dir && ((row && is_ring_cell(row-1,col))
		|| (!row && is_ring_cell(COLP,0))))
		|| (LEFT!=dir && ((col && is_ring_cell(row,col-1))
		|| (!col && row && is_ring_cell(0,ROWM))))
	) lattice[row][col] = FORBIDDEN; // adjacent to two ring cells
	else { // we can add a new child cell
		child_in[child_in_n] = {row, col, char(ring_index - quad * ring_cell_n),
			char(ring_index - quad * ring_cell_n), dir, 0};
		lattice[row][col] = FIRST_RING + ring_cell_n + child_in_n++;
	}
}
		
void Lattice::add_child_out(
	RowCol row,
	RowCol col
) {
	if (lattice[row][col]) return; // if already encountered
	child_out[child_out_n++] = {row, col};
	lattice[row][col] = OUTSIDE;
}
		
void Lattice::redel_in() { // requires child_in_count > 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;
	if (row || col) { // unused center cell left of lattice[0][0] for even rings
		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;
	}
	
	// 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};
		}
	} else if (row) { // unused center cell left of lattice[0][0] for even rings
		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
	pcount[0][next_parent_in]++;
}

void Lattice::add_parent_out() {
	parent_out[next_parent_out] = {(char)next_child_out, (char)child_out_n};
	int row = child_out[next_child_out].row; // coordinates of new parent cell
	int col = child_out[next_child_out].col;
	//child_out[next_child_out].parent_index = next_parent_out; // link from child to parent
	// add neighbors as children if empty
	if (EMPTY == lattice[row+1][col]) { // ABOVE
		lattice[row+1][col] = OUTSIDE;
		child_out[child_out_n++] = {(RowCol)(row+1), (RowCol)col};
	}
	if (EMPTY == lattice[row][col+1]) { // RIGHT
		lattice[row][col+1] = OUTSIDE;
		child_out[child_out_n++] = {(RowCol)row, (RowCol)(col+1)};
	}
	if (row) {
		if (EMPTY == lattice[row-1][col]) { // BELOW
			lattice[row-1][col] = OUTSIDE;
			child_out[child_out_n++] = {(RowCol)(row-1), (RowCol)col};
		}
	} else {
		if (EMPTY == lattice[COLP][0]) { // BELOW
			lattice[COLP][0] = OUTSIDE;
			child_out[child_out_n++] = {RowCol(COLP), 0};
		}
	}
	if (col) {
		if (EMPTY == lattice[row][col-1]) { // LEFT
			lattice[row][col-1] = OUTSIDE;
			child_out[child_out_n++] = {(RowCol)row, (RowCol)(col-1)};
		}
	} else {
		if (EMPTY == lattice[0][ROWM]) { // LEFT
			lattice[0][ROWM] = OUTSIDE;
			child_out[child_out_n++] = {0, RowCol(ROWM)};
		}
	}
	next_parent_out++;
	pcount[1][next_parent_out]++;
}

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
		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;
		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;
				} 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;
				}
				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() {
	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<NMAX; i++) {
		poly_count[ring_cell_n - 4][i][ring_is_chiral] += total[i];
	}
}
		
const void Lattice::dump_ring() {
	for (int i=0; i<next_parent_out; i++) {
		lattice[child_out[parent_out[i].child_index].row][child_out[parent_out[i].child_index].col] = PARENT;
	}
	for (int ri=NROW-1; ri>=0; ri-=1) { // for each row
		for (int ci=0; ci<NCOL; ci++) { // for each column
			int index = lattice[ri][ci];
			if (index>=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<next_parent_out; i++) {
		lattice[child_out[parent_out[i].child_index].row][child_out[parent_out[i].child_index].col] = OUTSIDE;
	}
}
/****************************************************************************************/
//
//  Lattice.hpp
//  ring_4c2_16p
//
//  Created by Robert A Russell on 10/16/21.
//  Copyright © 2021 Robert A Russell. All rights reserved.
//

#ifndef Lattice_hpp
#define Lattice_hpp

#include <iostream>
using std::cout;
using std::endl;
#include <set>
using std::set;
using std::pair;

#define NMAX 18 // maximum number of cells in quarter of polyomino; must be even
// 18 takes 33.1 secs; 20 takes 467.4 secs; 22 takes 6815.8 secs; 24 takes 29 hrs.
#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

typedef unsigned long long int Int;
typedef short LongChar; // used to make chars numeric
typedef unsigned short UnLongChar; // used to make chars numeric

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 {
	LongChar row;
	LongChar col;
	LongChar quadrant; // 0 for first, 1 for second, -1 for fourth
	LongChar prev_direction; // see directions above
	LongChar next_direction[3]; // possible directions of next ring cell
	LongChar 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}
	Int count[2]; // first is achiral, second chiral
	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
typedef unsigned short 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 UnLongChar RowCol; // change back to char later

typedef pair<RowCol,RowCol> CPair;
typedef set<CPair> CPairSet;
typedef set<CPairSet> 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
	LongChar quad; // quadrant of rotation (0 for quadrant shown)
} RingCell; // used in list of ring cells

typedef struct {
	RowCol row;
	RowCol col;
	LongChar low_ring; // index of associated ring cell, adjusted for rotation
	LongChar high_ring; // index of associated ring cell, adjusted for rotation
	UnLongChar dir; // direction of cell for which this is a child
	UnLongChar parent_index; // 1 + index of parent this child becomes, else zero
} ChildIn;

typedef struct {
	UnLongChar child_index; // index of child_in[]
	UnLongChar child_begin; // where new children of this parent begin in child_in
	LongChar low_ring; // index of associated ring cell, adjusted for rotation
	LongChar high_ring; // index of associated ring cell (could be zero!)
	UnLongChar parent_predecessor[4]; // 1+index of predecessor in parent_in[]
	UnLongChar 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; i<ring_cell_n; i++) { // fill in ring_cell[]
			unsigned char prev_dir=LEFT, next_dir=LEFT;
			switch (rlat.ring[i].prev_direction) {
				case R_UP: prev_dir = UP; break;
				case R_RIGHT: prev_dir = RIGHT; break;
				case R_DOWN: prev_dir = DOWN; break;
				default: break; // must be R_LEFT
			}
			switch (rlat.ring[i].next_direction[rlat.ring[i].next_direction_index]) {
				case R_UP: next_dir = UP; break;
				case R_RIGHT: next_dir = RIGHT; break;
				case R_DOWN: next_dir = DOWN; break;
				default: break; // must be R_LEFT
			}
			ring_cell[i] = {(RowCol)rlat.ring[i].row, (RowCol)rlat.ring[i].col,
				prev_dir, next_dir, (char)rlat.ring[i].quadrant};
			lattice[ring_cell[i].row][ring_cell[i].col] = FIRST_RING + i;
		}
		// check ring is okay??
	}
	void add_ring_children(); // create inside and outside children
	void redel_in();
	void redel_out();
	void count_poly();
	const bool any_child_in() {return child_in_n>0;}
	const void dump_ring();
};

void print_ring_count();
void print_poly_count();

#endif /* Lattice_hpp */
/****************************************************************************************/