/* * * * * * * * * * * * * * * * * * * * * *\
* O R D E R - 16  S Q U A R E  B U I L D E R *
\* * * * * * * * * * * * * * * * * * * * * */
var monagonalsImage = new Image(); monagonalsImage.src = "graphics/16monagonals.png";
var diagonalsImage = new Image(); diagonalsImage.src = "graphics/16diagonals.png";
var halfrowsImage = new Image(); halfrowsImage.src = "graphics/16halfrows.png";
var completeImage = new Image(); completeImage.src = "graphics/16complete.png";
var compactImage = new Image(); compactImage.src = "graphics/16compact.png";
var c3x3Image = new Image(); c3x3Image.src = "graphics/163x3.png";
var c5x5Image = new Image(); c5x5Image.src = "graphics/165x5.png";
var c9x9Image = new Image(); c9x9Image.src = "graphics/169x9.png";
var zigzag2Image = new Image(); zigzag2Image.src = "graphics/16zigzag2.png";
var zigzag3Image = new Image(); zigzag3Image.src = "graphics/16zigzag3.png";
var associatedImage = new Image(); associatedImage.src = "graphics/16associated.png";
var FranklinVImage = new Image(); FranklinVImage.src = "graphics/16FrankV.png";
var FranklinWImage = new Image(); FranklinWImage.src = "graphics/16FrankW.png";
var FranklinWWImage = new Image(); FranklinWWImage.src = "graphics/16FrankWW.png";
var order4Image = new Image(); order4Image.src = "graphics/16inlaid4.png";
var inlaid4AImage = new Image(); inlaid4AImage.src = "graphics/4inlaid4A.png";
var inlaid4BImage = new Image(); inlaid4BImage.src = "graphics/4inlaid4B.png";
var order8Image = new Image(); order8Image.src = "graphics/16eights.png";
var inlaid8Image = new Image(); inlaid8Image.src = "graphics/4inlaid8.png";
var center8Image = new Image(); center8Image.src = "graphics/4center8.png";
var blankImage = new Image(); blankImage.src = "graphics/16blank.png";

//The initial order-16 magic square on the page
var tableBases = new Array(
	new Array(  1,   6,   7,   4,  25,  30,  31,  28, 225, 230, 231, 228, 249, 254, 255, 252),
	new Array(155, 160, 157, 154, 131, 136, 133, 130, 123, 128, 125, 122,  99, 104, 101,  98),
	new Array( 73,  78,  79,  76,  81,  86,  87,  84, 169, 174, 175, 172, 177, 182, 183, 180),
	new Array(211, 216, 213, 210, 203, 208, 205, 202,  51,  56,  53,  50,  43,  48,  45,  42),
	new Array( 34,  37,  40,  35,  58,  61,  64,  59, 194, 197, 200, 195, 218, 221, 224, 219),
	new Array(188, 191, 190, 185, 164, 167, 166, 161,  92,  95,  94,  89,  68,  71,  70,  65),
	new Array(106, 109, 112, 107, 114, 117, 120, 115, 138, 141, 144, 139, 146, 149, 152, 147),
	new Array(244, 247, 246, 241, 236, 239, 238, 233,  20,  23,  22,  17,  12,  15,  14,   9),
	new Array(  5,   2,   3,   8,  29,  26,  27,  32, 229, 226, 227, 232, 253, 250, 251, 256),
	new Array(159, 156, 153, 158, 135, 132, 129, 134, 127, 124, 121, 126, 103, 100,  97, 102),
	new Array( 77,  74,  75,  80,  85,  82,  83,  88, 173, 170, 171, 176, 181, 178, 179, 184),
	new Array(215, 212, 209, 214, 207, 204, 201, 206,  55,  52,  49,  54,  47,  44,  41,  46),
	new Array( 38,  33,  36,  39,  62,  57,  60,  63, 198, 193, 196, 199, 222, 217, 220, 223),
	new Array(192, 187, 186, 189, 168, 163, 162, 165,  96,  91,  90,  93,  72,  67,  66,  69),
	new Array(110, 105, 108, 111, 118, 113, 116, 119, 142, 137, 140, 143, 150, 145, 148, 151),
	new Array(248, 243, 242, 245, 240, 235, 234, 237,  24,  19,  18,  21,  16,  11,  10,  13)
);

var masterBaseLines = new Array(
	new Array(  0,  5,  6,  3, 24, 29, 30, 27,224,229,230,227,248,253,254,251),
	new Array(  0,154, 72,210, 33,187, 105,243, 4,158, 76,214, 37,191,109,247)
);

var featureList = new Array('selectcompact','select3x3','select5x5','select9x9','selectcomplete','selectassociated','selectzigzag2','selectzigzag3','selectfranklinv','selectfranklinw','selectfranklinww','selectorder4','selectinlaid4A','selectinlaid4B','selectorder8','selectinlaid8','selectcenter8','selecthalfrows');

var baseLines = new Array;//list of all valid base lines
var baseSquares = new Array();//an array of all 2868 base squares.
var baseSquareSet = baseSquares;//base squares valid for all selected features
var features = new Array;//mask for all features present in base squares
var matrix = new Array();//position in baseSquares of checked boxes

//Show selected image on rollover.
function changeImage(picture)
{
	var td = document.getElementById("squareImage");
	td.removeChild(td.childNodes[1]);
	td.appendChild(picture);
}

//Creates valid base lines.
function getBaseLines()
{
	baseLines.push(21845);
	baseLines.push(13107);
	baseLines.push(26214);
	for (var i = 0; i<8; i++)
	{
			baseLines.push(3855*(i+1));
	}
	for (var i = 0; i<128; i++)
	{
			baseLines.push(255*(i+1));
	}
}

//Determines if base squares are valid.
function validateSquare(square)
{
	for (var v = 0; v < 16; v++)
	{
		var sum = 0;
		for (var w = 0; w < 16; w++)
		{
			sum += square[w][(v+w)%16];
		}
		if (sum != 8)
		{
			return false;
		}
		var sum = 0;
		for (var w = 0; w < 16; w++)
		{
			sum += square[w][(15-w+v)%16];
		}
		if (sum != 8)
		{
			return false;
		}
	}
	return true;
}

//Creates valid base squares.
function getBaseSquares()
{
	for(var i = 0; i < 11; i++)
	{
		for(var j = i+1; j < baseLines.length; j++)
		{
			var square = new Array();
			for(var y = 0; y < 16; y++)
			{
				var line = new Array();
				for(var x = 0; x < 16; x++)
				{
					line.push(((baseLines[i] >> (15-y)) & 1) ^ ((baseLines[j] >> (15-x)) & 1));
				}
				square.push(line);
			}
			if (validateSquare(square))
			{
				baseSquares.push(square);
				var square = new Array();
				for(var y = 0; y < 16; y++)
				{
					var line = new Array();
					for(var x = 0; x < 16; x++)
					{
						line.push(((baseLines[j] >> (15-y)) & 1) ^ ((baseLines[i] >> (15-x)) & 1));
					}
					square.push(line);
				}
				baseSquares.push(square);
			}
		}
	}
}

//Creates compact mask. Just checks 1/4 of instances.
function getCompact(square)
{
	for(var y = 0; y < 8; y++)
	{
		for(var x = 0; x < 8; x++)
		{
			var sum = square[x][y] + square[(x+1)%16][y] + square[x][(y+1)%16] + square[(x+1)%16][(y+1)%16];
			if (sum !=2)
			{
				return false;
			}
		}
	}
	return true;
}

//Creates compact3 mask. Just checks 1/4 of instances.
function getCompact3(square)
{
	for(var y = 0; y < 8; y++)
	{
		for(var x = 0; x < 8; x++)
		{
			var sum = square[x][y] + square[(x+2)%16][y] + square[x][(y+2)%16] + square[(x+2)%16][(y+2)%16];
			if (sum !=2)
			{
				return false;
			}
		}
	}
	return true;
}

//Creates compact5 mask. Just checks 1/4 of instances.
function getCompact5(square)
{
	for(var y = 0; y < 8; y++)
	{
		for(var x = 0; x < 8; x++)
		{
			var sum = square[x][y] + square[(x+4)%16][y] + square[x][(y+4)%16] + square[(x+4)%16][(y+4)%16];
			if (sum !=2)
			{
				return false;
			}
		}
	}
	return true;
}

//Creates compact9 mask. Just checks 1/4 of instances.
function getCompact9(square)
{
	for(var y = 0; y < 8; y++)
	{
		for(var x = 0; x < 8; x++)
		{
			var sum = square[x][y] + square[(x+8)%16][y] + square[x][(y+8)%16] + square[(x+8)%16][(y+8)%16];
			if (sum !=2)
			{
				return false;
			}
		}
	}
	return true;
}

//Creates associated mask. Just checks 1/4 of instances.
function getAssociated(square)
{
	for(var y = 0; y < 4; y++)
	{
		for(var x = 0; x < 8; x++)
		{
			var sum = square[x][y] + square[15-x][15-y];
			if (sum !=1)
			{
				return false;
			}
		}
	}
	return true;
}

//Creates zigzag2 mask. Just checks 1/4 of instances.
function getZigzag(square)
{
	for(var y = 0; y < 8; y++)
	{
		for(var x = 0; x < 1; x++)
		{
			var sum = square[x][y] + square[(x+1)%16][(y+1)%16] + square[(x+2)%16][y] + square[(x+3)%16][(y+1)%16] + square[(x+4)%16][y] + square[(x+5)%16][(y+1)%16] + square[(x+6)%16][y] + square[(x+7)%16][(y+1)%16] + square[x+8][y] + square[(x+9)%16][(y+1)%16] + square[(x+10)%16][y] + square[(x+11)%16][(y+1)%16] + square[(x+12)%16][y] + square[(x+13)%16][(y+1)%16] + square[(x+14)%16][y] + square[(x+15)%16][(y+1)%16];
			if (sum !=8)
			{
				return false;
			}
			var sum = square[y][x] + square[(y+1)%16][(x+1)%16] + square[y][(x+2)%16] + square[(y+1)%16][(x+3)%16] + square[y][(x+4)%16] + square[(y+1)%16][(x+5)%16] + square[y][(x+6)%16] + square[(y+1)%16][(x+7)%16] + square[y][x+8] + square[(y+1)%16][(x+9)%16] + square[y][(x+10)%16] + square[(y+1)%16][(x+11)%16] + square[y][(x+12)%16] + square[(y+1)%16][(x+13)%16] + square[y][(x+14)%16] + square[(y+1)%16][(x+15)%16];
			if (sum !=8)
			{
				return false;
			}
		}
	}
	return true;
}

//Creates zigzag3 mask. Just checks 1/4 of instances.
function getZigzag3(square)
{
	for(var y = 0; y < 8; y++)
	{
		for(var x = 0; x < 2; x++)
		{
			var sum = square[x][y] + square[(x+1)%16][(y+1)%16] + square[(x+2)%16][(y+2)%16] + square[(x+3)%16][(y+1)%16] + square[(x+4)%16][y] + square[(x+5)%16][(y+1)%16] + square[(x+6)%16][(y+2)%16] + square[(x+7)%16][(y+1)%16] + square[x+8][y] + square[(x+9)%16][(y+1)%16] + square[(x+10)%16][(y+2)%16] + square[(x+11)%16][(y+1)%16] + square[(x+12)%16][y] + square[(x+13)%16][(y+1)%16] + square[(x+14)%16][(y+2)%16] + square[(x+15)%16][(y+1)%16];
			if (sum !=8)
			{
				return false;
			}
			var sum = square[y][x] + square[(y+1)%16][(x+1)%16] + square[(y+2)%16][(x+2)%16] + square[(y+1)%16][(x+3)%16] + square[y][(x+4)%16] + square[(y+1)%16][(x+5)%16] + square[(y+2)%16][(x+6)%16] + square[(y+1)%16][(x+7)%16] + square[y][x+8] + square[(y+1)%16][(x+9)%16] + square[(y+2)%16][(x+10)%16] + square[(y+1)%16][(x+11)%16] + square[y][(x+12)%16] + square[(y+1)%16][(x+13)%16] + square[(y+2)%16][(x+14)%16] + square[(y+1)%16][(x+15)%16];
			if (sum !=8)
			{
				return false;
			}
		}
	}
	return true;
}

//Creates FranklinV mask. Just checks 1/4 of instances.
function getFranklinV(square)
{
	for(var x = 0; x < 4; x++)
	{
		var sum = square[x][0] + square[(x+1)%16][1] + square[(x+2)%16][2] + square[(x+3)%16][3] + square[(x+4)%16][4] + square[(x+5)%16][5] + square[(x+6)%16][6] + square[(x+7)%16][7] + square[(x+7)%16][8] + square[(x+6)%16][9] + square[(x+5)%16][10] + square[(x+4)%16][11] + square[(x+3)%16][12] + square[(x+2)%16][13] + square[(x+1)%16][14] + square[x][15];
		if (sum !=8)
		{
			return false;
		}
		var sum = square[x][0] + square[(x+15)%16][1] + square[(x+14)%16][2] + square[(x+13)%16][3] + square[(x+12)%16][4] + square[(x+11)%16][5] + square[(x+10)%16][6] + square[(x+9)%16][7] + square[(x+9)%16][8] + square[(x+10)%16][9] + square[(x+11)%16][10] + square[(x+12)%16][11] + square[(x+13)%16][12] + square[(x+14)%16][13] + square[(x+15)%16][14] + square[x][15];
		if (sum !=8)
		{
			return false;
		}
		var sum = square[0][x] + square[1][(x+1)%16] + square[2][(x+2)%16] + square[3][(x+3)%16] + square[4][(x+4)%16] + square[5][(x+5)%16] + square[6][(x+6)%16] + square[7][(x+7)%16] + square[8][(x+7)%16] + square[9][(x+6)%16] + square[10][(x+5)%16] + square[11][(x+4)%16] + square[12][(x+3)%16] + square[13][(x+2)%16] + square[14][(x+1)%16] + square[15][x];
		if (sum !=8)
		{
			return false;
		}
		var sum = square[0][x] + square[1][(x+15)%16] + square[2][(x+14)%16] + square[3][(x+13)%16] + square[4][(x+12)%16] + square[5][(x+11)%16] + square[6][(x+10)%16] + square[7][(x+9)%16] + square[8][(x+9)%16] + square[9][(x+10)%16] + square[10][(x+11)%16] + square[11][(x+12)%16] + square[12][(x+13)%16] + square[13][(x+14)%16] + square[14][(x+15)%16] + square[15][x];
		if (sum !=8)
		{
			return false;
		}
	}
	return true;
}

//Creates FranklinW mask. Just checks 1/2 of instances.
function getFranklinW(square)
{
	for(var x = 0; x < 8; x++)
	{
		var sum = square[x][0] + square[(x+1)%16][1] + square[(x+2)%16][2] + square[(x+3)%16][3] + square[(x+3)%16][4] + square[(x+2)%16][5] + square[(x+1)%16][6] + square[x][7] + square[x][8] + square[(x+1)%16][9] + square[(x+2)%16][10] + square[(x+3)%16][11] + square[(x+3)%16][12] + square[(x+2)%16][13] + square[(x+1)%16][14] + square[x][15];
		if (sum !=8)
		{
			return false;
		}
		var sum = square[x][0] + square[(x+15)%16][1] + square[(x+14)%16][2] + square[(x+13)%16][3] + square[(x+13)%16][4] + square[(x+14)%16][5] + square[(x+15)%16][6] + square[x][7] + square[x][8] + square[(x+15)%16][9] + square[(x+14)%16][10] + square[(x+13)%16][11] + square[(x+13)%16][12] + square[(x+14)%16][13] + square[(x+15)%16][14] + square[x][15];
		if (sum !=8)
		{
			return false;
		}
		var sum = square[0][x] + square[1][(x+1)%16] + square[2][(x+2)%16] + square[3][(x+3)%16] + square[4][(x+3)%16] + square[5][(x+2)%16] + square[6][(x+1)%16] + square[7][x] + square[8][x] + square[9][(x+1)%16] + square[10][(x+2)%16] + square[11][(x+3)%16] + square[12][(x+3)%16] + square[13][(x+2)%16] + square[14][(x+1)%16] + square[15][x];
		if (sum !=8)
		{
			return false;
		}
		var sum = square[0][x] + square[1][(x+15)%16] + square[2][(x+14)%16] + square[3][(x+13)%16] + square[4][(x+13)%16] + square[5][(x+14)%16] + square[6][(x+15)%16] + square[7][x] + square[8][x] + square[9][(x+15)%16] + square[10][(x+14)%16] + square[11][(x+13)%16] + square[12][(x+13)%16] + square[13][(x+14)%16] + square[14][(x+15)%16] + square[15][x];
		if (sum !=8)
		{
			return false;
		}
	}
	return true;
}

//Creates FranklinWW mask. Just checks 1/2 of instances.
function getFranklinWW(square)
{
	for(var x = 0; x < 8; x++)
	{
		var sum = square[x][0] + square[(x+1)%16][1] + square[(x+1)%16][2] + square[x][3] + square[x][4] + square[(x+1)%16][5] + square[(x+1)%16][6] + square[x][7] + square[x][8] + square[(x+1)%16][9] + square[(x+1)%16][10] + square[x][11] + square[x][12] + square[(x+1)%16][13] + square[(x+1)%16][14] + square[x][15];
		if (sum !=8)
		{
			return false;
		}
		var sum = square[x][0] + square[(x+15)%16][1] + square[(x+15)%16][2] + square[x][3] + square[x][4] + square[(x+15)%16][5] + square[(x+15)%16][6] + square[x][7] + square[x][8] + square[(x+15)%16][9] + square[(x+15)%16][10] + square[x][11] + square[x][12] + square[(x+15)%16][13] + square[(x+15)%16][14] + square[x][15];
		if (sum !=8)
		{
			return false;
		}
		var sum = square[0][x] + square[1][(x+1)%16] + square[2][(x+1)%16] + square[3][x] + square[4][x] + square[5][(x+1)%16] + square[6][(x+1)%16] + square[7][x] + square[8][x] + square[9][(x+1)%16] + square[10][(x+1)%16] + square[11][x] + square[12][x] + square[13][(x+1)%16] + square[14][(x+1)%16] + square[15][x];
		if (sum !=8)
		{
			return false;
		}
		var sum = square[0][x] + square[1][(x+15)%16] + square[2][(x+15)%16] + square[3][x] + square[4][x] + square[5][(x+15)%16] + square[6][(x+15)%16] + square[7][x] + square[8][x] + square[9][(x+15)%16] + square[10][(x+15)%16] + square[11][x] + square[12][x] + square[13][(x+15)%16] + square[14][(x+15)%16] + square[15][x];
		if (sum !=8)
		{
			return false;
		}
	}
	return true;
}

//Creates order4 mask. Just checks 1/2 of instances.
function getOrder4(square)
{
	for(var x = 0; x < 2; x++)
	{
		var sum = square[x][0] + square[x][1] + square[x][2] + square[x][3];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[0][x] + square[1][x] + square[2][x] + square[3][x];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[0][x] + square[1][(x+1)%4] + square[2][(x+2)%4] + square[3][(x+3)%4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[0][(8-x)%4] + square[1][(7-x)%4] + square[2][(6-x)%4] + square[3][(5-x)%4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[x][4] + square[x][5] + square[x][6] + square[x][7];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[0][x+4] + square[1][x+4] + square[2][x+4] + square[3][x+4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[0][x+4] + square[1][((x+1)%4)+4] + square[2][((x+2)%4)+4] + square[3][((x+3)%4)+4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[0][((8-x)%4)+4] + square[1][((7-x)%4)+4] + square[2][((6-x)%4)+4] + square[3][((5-x)%4)+4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[x+4][0] + square[x+4][1] + square[x+4][2] + square[x+4][3];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[4][x] + square[5][x] + square[6][x] + square[7][x];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[4][x] + square[5][(x+1)%4] + square[6][(x+2)%4] + square[7][(x+3)%4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[4][(8-x)%4] + square[5][(7-x)%4] + square[6][(6-x)%4] + square[7][(5-x)%4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[x+4][4] + square[x+4][5] + square[x+4][6] + square[x+4][7];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[4][x+4] + square[5][x+4] + square[6][x+4] + square[7][x+4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[4][x+4] + square[5][((x+1)%4)+4] + square[6][((x+2)%4)+4] + square[7][((x+3)%4)+4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[4][((8-x)%4)+4] + square[5][((7-x)%4)+4] + square[6][((6-x)%4)+4] + square[7][((5-x)%4)+4];
		if (sum !=2)
		{
			return false;
		}
	}
	return true;
}

//Creates inlaid4A mask. Just checks 1/2 of instances.
function getInlaid4A(square)
{
	for(var x = 0; x < 2; x++)
	{
		var sum = square[x][0] + square[x][1] + square[x][2] + square[x][3];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[0][x] + square[1][x] + square[2][x] + square[3][x];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[0][x] + square[1][(x+1)%4] + square[2][(x+2)%4] + square[3][(x+3)%4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[0][(8-x)%4] + square[1][(7-x)%4] + square[2][(6-x)%4] + square[3][(5-x)%4];
		if (sum !=2)
		{
			return false;
		}
	}
	return true;
}

//Creates inlaid4B mask. Just checks 1/2 of instances.
function getInlaid4B(square)
{
	for(var x = 0; x < 2; x++)
	{
		var sum = square[x+4][4] + square[x+4][5] + square[x+4][6] + square[x+4][7];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[4][x+4] + square[5][x+4] + square[6][x+4] + square[7][x+4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[4][x+4] + square[5][((x+1)%4)+4] + square[6][((x+2)%4)+4] + square[7][((x+3)%4)+4];
		if (sum !=2)
		{
			return false;
		}
		var sum = square[4][((8-x)%4)+4] + square[5][((7-x)%4)+4] + square[6][((6-x)%4)+4] + square[7][((5-x)%4)+4];
		if (sum !=2)
		{
			return false;
		}
	}
	return true;
}

//Creates order8 mask. Just checks 1/4 of instances.
function getOrder8(square)
{
	for(var x = 0; x < 2; x++)
	{
		var sum = square[x][0] + square[x][1] + square[x][2] + square[x][3] + square[x][4] + square[x][5] + square[x][6] + square[x][7];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[0][x] + square[1][x] + square[2][x] + square[3][x] + square[4][x] + square[5][x] + square[6][x] + square[7][x];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[0][x] + square[1][(x+1)%8] + square[2][(x+2)%8] + square[3][(x+3)%8] + square[4][(x+4)%8] + square[5][(x+5)%8] + square[6][(x+6)%8] + square[7][(x+7)%8];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[0][(16-x)%8] + square[1][(15-x)%8] + square[2][(14-x)%8] + square[3][(13-x)%8] + square[4][(12-x)%8] + square[5][(11-x)%8] + square[6][(10-x)%8] + square[7][(9-x)%8];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[x][4] + square[x][5] + square[x][6] + square[x][7] + square[x][8] + square[x][9] + square[x][10] + square[x][11];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[0][x+4] + square[1][x+4] + square[2][x+4] + square[3][x+4] + square[4][x+4] + square[5][x+4] + square[6][x+4] + square[7][x+4];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[0][x+4] + square[1][((x+1)%8)+4] + square[2][((x+2)%8)+4] + square[3][((x+3)%8)+4] + square[4][((x+4)%8)+4] + square[5][((x+5)%8)+4] + square[6][((x+6)%8)+4] + square[7][((x+7)%8)+4];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[0][((16-x)%8)+4] + square[1][((15-x)%8)+4] + square[2][((14-x)%8)+4] + square[3][((13-x)%8)+4] + square[4][((12-x)%8)+4] + square[5][((11-x)%8)+4] + square[6][((10-x)%8)+4] + square[7][((9-x)%8)+4];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[x+4][0] + square[x+4][1] + square[x+4][2] + square[x+4][3] + square[x+4][4] + square[x+4][5] + square[x+4][6] + square[x+4][7];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[4][x] + square[5][x] + square[6][x] + square[7][x] + square[8][x] + square[9][x] + square[10][x] + square[11][x];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[4][x] + square[5][(x+1)%8] + square[6][(x+2)%8] + square[7][(x+3)%8] + square[8][(x+4)%8] + square[9][(x+5)%8] + square[10][(x+6)%8] + square[11][(x+7)%8];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[4][(16-x)%8] + square[5][(15-x)%8] + square[6][(14-x)%8] + square[7][(13-x)%8] + square[8][(12-x)%8] + square[9][(11-x)%8] + square[10][(10-x)%8] + square[11][(9-x)%8];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[x+4][4] + square[x+4][5] + square[x+4][6] + square[x+4][7] + square[x+4][8] + square[x+4][9] + square[x+4][10] + square[x+4][11];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[4][x+4] + square[5][x+4] + square[6][x+4] + square[7][x+4] + square[8][x+4] + square[9][x+4] + square[10][x+4] + square[11][x+4];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[4][x+4] + square[5][((x+1)%8)+4] + square[6][((x+2)%8)+4] + square[7][((x+3)%8)+4] + square[8][((x+4)%8)+4] + square[9][((x+5)%8)+4] + square[10][((x+6)%8)+4] + square[11][((x+7)%8)+4];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[4][((16-x)%8)+4] + square[5][((15-x)%8)+4] + square[6][((14-x)%8)+4] + square[7][((13-x)%8)+4] + square[8][((12-x)%8)+4] + square[9][((11-x)%8)+4] + square[10][((10-x)%8)+4] + square[11][((9-x)%8)+4];
		if (sum !=4)
		{
			return false;
		}
	}
	return true;
}

//Creates inlaid8 mask. Just checks 1/2 of instances.
function getInlaid8(square)
{
	for(var x = 0; x < 4; x++)
	{
		var sum = square[x][0] + square[x][1] + square[x][2] + square[x][3] + square[x][4] + square[x][5] + square[x][6] + square[x][7];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[0][x] + square[1][x] + square[2][x] + square[3][x] + square[4][x] + square[5][x] + square[6][x] + square[7][x];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[0][x] + square[1][(x+1)%8] + square[2][(x+2)%8] + square[3][(x+3)%8] + square[4][(x+4)%8] + square[5][(x+5)%8] + square[6][(x+6)%8] + square[7][(x+7)%8];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[0][(16-x)%8] + square[1][(15-x)%8] + square[2][(14-x)%8] + square[3][(13-x)%8] + square[4][(12-x)%8] + square[5][(11-x)%8] + square[6][(10-x)%8] + square[7][(9-x)%8];
		if (sum !=4)
		{
			return false;
		}
	}
	return true;
}

//Creates center8 mask. Just checks 1/2 of instances.
function getCenter8(square)
{
	for(var x = 0; x < 4; x++)
	{
		var sum = square[x+4][4] + square[x+4][5] + square[x+4][6] + square[x+4][7] + square[x+4][8] + square[x+4][9] + square[x+4][10] + square[x+4][11];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[4][x+4] + square[5][x+4] + square[6][x+4] + square[7][x+4] + square[8][x+4] + square[9][x+4] + square[10][x+4] + square[11][x+4];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[4][x+4] + square[5][((x+1)%8)+4] + square[6][((x+2)%8)+4] + square[7][((x+3)%8)+4] + square[8][((x+4)%8)+4] + square[9][((x+5)%8)+4] + square[10][((x+6)%8)+4] + square[11][((x+7)%8)+4];
		if (sum !=4)
		{
			return false;
		}
		var sum = square[4][((16-x)%8)+4] + square[5][((15-x)%8)+4] + square[6][((14-x)%8)+4] + square[7][((13-x)%8)+4] + square[8][((12-x)%8)+4] + square[9][((11-x)%8)+4] + square[10][((10-x)%8)+4] + square[11][((9-x)%8)+4];
		if (sum !=4)
		{
			return false;
		}
	}
	return true;
}

//Creates halfrows mask.
function getHalfRows(square)
{
	for(var x = 0; x < 16; x++)
	{
		var sum1 = square[x][0] + square[x][1] + square[x][2] + square[x][3] + square[x][4] + square[x][5] + square[x][6] + square[x][7];
		var sum2 = square[0][x] + square[1][x] + square[2][x] + square[3][x] + square[4][x] + square[5][x] + square[6][x] + square[7][x];
		if (sum1 !=4 || sum2 !=4)
		{
			return false;
		}
	}
	return true;
}


//Creates all valid base squares and makes feature mask.
function loadBaseSquares()
{
	getBaseLines();
	getBaseSquares();
	baseSquareSet = baseSquares;
	for (var i=0; i<18; i++)
		{
			features.push(new Array());
		}
	for(var i = 0; i < baseSquares.length; i++)
	{
		if (getCompact(baseSquares[i]))
		{
			features[0].push(1);
		}
		else
		{
			features[0].push(0);
		}
		if (getCompact3(baseSquares[i]))
		{
			features[1].push(1);
		}
		else
		{
			features[1].push(0);
		}
		if (getCompact5(baseSquares[i]))
		{
			features[2].push(1);
		}
		else
		{
			features[2].push(0);
		}
		if (getCompact9(baseSquares[i]))//and complete
		{
			features[3].push(1);
			features[4].push(1);
		}
		else
		{
			features[3].push(0);
			features[4].push(0);
		}
		if (getAssociated(baseSquares[i]))
		{
			features[5].push(1);
		}
		else
		{
			features[5].push(0);
		}
		if (getZigzag(baseSquares[i]))
		{
			features[6].push(1);
		}
		else
		{
			features[6].push(0);
		}
		if (getZigzag3(baseSquares[i]))
		{
			features[7].push(1);
		}
		else
		{
			features[7].push(0);
		}
		if (getFranklinV(baseSquares[i]))
		{
			features[8].push(1);
		}
		else
		{
			features[8].push(0);
		}
		if (getFranklinW(baseSquares[i]))
		{
			features[9].push(1);
		}
		else
		{
			features[9].push(0);
		}
		if (getFranklinWW(baseSquares[i]))
		{
			features[10].push(1);
		}
		else
		{
			features[10].push(0);
		}
		if (getOrder4(baseSquares[i]))
		{
			features[11].push(1);
		}
		else
		{
			features[11].push(0);
		}
		if (getInlaid4A(baseSquares[i]))
		{
			features[12].push(1);
		}
		else
		{
			features[12].push(0);
		}
		if (getInlaid4B(baseSquares[i]))
		{
			features[13].push(1);
		}
		else
		{
			features[13].push(0);
		}
		if (getOrder8(baseSquares[i]))
		{
			features[14].push(1);
		}
		else
		{
			features[14].push(0);
		}
		if (getInlaid8(baseSquares[i]))
		{
			features[15].push(1);
		}
		else
		{
			features[15].push(0);
		}
		if (getCenter8(baseSquares[i]))
		{
			features[16].push(1);
		}
		else
		{
			features[16].push(0);
		}
		if (getHalfRows(baseSquares[i]))
		{
			features[17].push(1);
		}
		else
		{
			features[17].push(0);
		}
	}
}

//Makes it impossible to check the associated box.
function disableBox(boxId)
{
	document.getElementById(boxId).disabled = true;
}

//Makes it possible to check the associated box.
function enableBox(boxId)
{
	document.getElementById(boxId).disabled = false;
}

//Unchecks the associated box.
function uncheckBox(boxId)
{
	document.getElementById(boxId).checked = false;
}

//Determines number of base squares selected and assigns them a sequence number.
function enumerateChecked(boxId,matrixNum)
{
	if (document.getElementById(boxId).checked == true)
	{
		matrix.push(matrixNum);
	}
}

//Determines if the current square has even integral distribution.
function consecutive(tableBases, base)
{
	var count = new Array();
	for (var x=0; x < (1 << base); x++)
	{
		count.push(0);
	}
	for (var y=0; y<16; y++)
	{
		for (var x=0; x<16; x++)
		{
			var val = tableBases[y][x];
			if(count[val] < (256 >> base))
			{
				count[val]++;
			}
			else
			{
				return false;
			}
		}
	}
	return true;
}

function findLargeCombo(outSquare, oldMagicSquare, oldBaseSquareSet, depth)
{
	// initialize
	var magicSquare = new Array();
	var baseSquareSet = oldBaseSquareSet.slice();
	for(var y = 0; y < 16; y++)
	{
		magicSquare.push(new Array());
		for(var x = 0; x < 16; x++)
		{
			magicSquare[y].push(0);
		}
	}
	// iterate through possible base squares to eliminate incompatible ones
	for(var i = 0; i < baseSquareSet.length;)
	{
		var baseSquare = baseSquareSet[i];
		for(var y = 0; y < 16; y++)
		{
			for(var x = 0; x < 16; x++)
			{
				magicSquare[y][x] = oldMagicSquare[y][x] | baseSquare[y][x];
			}
		}
		if(consecutive(magicSquare, depth))
		{
			i++;
		}
		else
		{
			baseSquareSet.splice(i, 1);
		}
	}
	// iterate through valid base squares randomly until final magic
	// square is found
	while(baseSquareSet.length > 0)
	{
		// random selector
		var randomIndex = Math.floor(Math.random() * baseSquareSet.length);
		var baseSquare = baseSquareSet[randomIndex];
		// build master base square
		for(var y = 0; y < 16; y++)
		{
			for(var x = 0; x < 16; x++)
			{
				magicSquare[y][x] = oldMagicSquare[y][x] | baseSquare[y][x];
			}
		}
		// test for validity
		if(depth < 8)
		{
			for(var y = 0; y < 16; y++)
			{
				for(var x = 0; x < 16; x++)
				{
					magicSquare[y][x] <<= 1;
				}
			}
			if(findLargeCombo(outSquare, magicSquare, baseSquareSet, depth + 1))
			{
				return true;
			}
			else
			{
				baseSquareSet.splice(randomIndex, 1);
			}
		}
		else
		{
			// copy
			for(var y = 0; y < 16; y++)
			{
				for(var x = 0; x < 16; x++)
				{
					outSquare[y][x] = magicSquare[y][x];
				}
			}
			return true;
		}
	}
	return false;
}

function findCombo(outSquare, oldMagicSquare, oldBaseSquareSet, depth)
{
	// initialize
	var magicSquare = new Array();
	var baseSquareSet = oldBaseSquareSet.slice();
	for(var y = 0; y < 16; y++)
	{
		magicSquare.push(new Array());
		for(var x = 0; x < 16; x++)
		{
			magicSquare[y].push(0);
		}
	}
	// iterate through possible base squares until a valid one is found
	while(baseSquareSet.length > 0)
	{
		var randomIndex = Math.floor(Math.random() * baseSquareSet.length);
		var baseSquare = baseSquareSet[randomIndex];
		for(var y = 0; y < 16; y++)
		{
			for(var x = 0; x < 16; x++)
			{
				magicSquare[y][x] = oldMagicSquare[y][x] | baseSquare[y][x];
			}
		}
		if(consecutive(magicSquare, depth))
		{
			// test for validity
			if(depth < 8)
			{
				for(var y = 0; y < 16; y++)
				{
					for(var x = 0; x < 16; x++)
					{
						magicSquare[y][x] <<= 1;
					}
				}
				if(depth < 4)
				{
					if(findCombo(outSquare, magicSquare, baseSquareSet, depth + 1))
					{
						return true;
					}
					else
					{
						baseSquareSet.splice(randomIndex, 1);
					}
				}
				else
				{
					if(findLargeCombo(outSquare, magicSquare, baseSquareSet, depth + 1))
					{
						return true;
					}
					else
					{
						baseSquareSet.splice(randomIndex, 1);
					}
				}
			}
			else
			{
				// copy
				for(var y = 0; y < 16; y++)
				{
					for(var x = 0; x < 16; x++)
					{
						outSquare[y][x] = magicSquare[y][x];
					}
				}
				return true;
			}
		}
		else
		{
			baseSquareSet.splice(randomIndex, 1);
		}
	}
	return false;
}


//new
//Determines if features selected can be checked and still yield a valid square.
function checkableFeatures(boxId,matrixNum)
{
	if(!document.getElementById(boxId).checked && !document.getElementById(boxId).disabled)
	{
		baseSquareSet = new Array();
		matrix.push(matrixNum);
		// gather valid base squares into baseSquareSet
		for (var y=0; y<(baseSquares.length); y++)
		{
			var isValid = true;
			for (var x = 0; x < matrix.length; x++)
			{
				if (features[matrix[x]][y] == 0)
				{
					isValid = false;
					break;
				}
			}
			if (isValid)
			{
				baseSquareSet.push(baseSquares[y]);
			}
		}
		//failure to have enough base squares
		if (baseSquareSet.length < 8)
		{
			disableBox(boxId);
		}
		//determine if there is more than one position that is always zero
		else
		{
	var featureSquare = new Array();
			for(var z = 0; z < 16; z++)
			{
				for(var y = 0; y < 16; y++)
				{
					var sum = 0;
					for(var x = 0; x < baseSquareSet.length; x++)
					{
						sum += baseSquareSet[x][y][z];
						if (sum > 0)
							{
								break;
							}
					}
					featureSquare.push(sum);
					if ((y + z > 0) && sum == 0)
					{
						disableBox(boxId);
						matrix.pop();		
						return false;
					}
				}
			}
		}
		matrix.pop();
	}
}

//Writes the new magic square on the page.
function updateTable()
{
	for (var y=0; y<16; y++) {
		document.getElementById('masterx' + y + 'y' + 0).innerHTML = tableBases[0][y];
		document.getElementById('masterx' + y + 'y' + 1).innerHTML = tableBases[y][0];
		for (var x=0; x<16; x++) {
			document.getElementById('magicx' + x + 'y' + y).innerHTML = (tableBases[y][x] + 1);
		}
	}
}

//Builds a new magic square with the base squares selected.
function build(boxChecked)
{
	var magicSquare = new Array();
	for(var y = 0; y < 16; y++)
	{
		magicSquare.push(new Array());
		for(var x = 0; x < 16; x++)
		{
			magicSquare[y].push(0);
		}
	}
	if(findCombo(tableBases, magicSquare, baseSquareSet, 1))
	{
		updateTable();
	}
	else
	{
		document.getElementById(boxChecked).checked = false;
		disableBox(boxChecked);
		alert("Search was unsuccessful. It is not possible to make a square with this combination of features and the restrictions of this builder.");
	}
}

//Determines which boxes are checked and which boxes are disabled after each check or uncheck.
function checkCompatibility(boxChecked)
{
	for (var index=0; index<featureList.length; index++)
	{
		enableBox(featureList[index]);
	}
	if (document.getElementById(boxChecked).checked == true && (boxChecked == 'selectcomplete' || boxChecked == 'select9x9'))
	{
		document.getElementById('selectcomplete').checked = true;
		document.getElementById('select9x9').checked = true;
	}
	if (document.getElementById(boxChecked).checked == false && (boxChecked == 'selectcomplete' || boxChecked == 'select9x9'))
	{
		document.getElementById('selectcomplete').checked = false;
		document.getElementById('select9x9').checked = false;
	}
	if (document.getElementById('selectorder4').checked == false && boxChecked == 'selectorder4')
	{
		document.getElementById('selectinlaid4A').checked = false;
		document.getElementById('selectinlaid4B').checked = false;
	}
	if (document.getElementById('selectorder4').checked == true && document.getElementById('selectinlaid4A').checked == false && document.getElementById('selectinlaid4B').checked == true && boxChecked == 'selectinlaid4A')
	{
		document.getElementById('selectorder4').checked = false;
	}
	if (document.getElementById('selectorder4').checked == true && document.getElementById('selectinlaid4A').checked == true && document.getElementById('selectinlaid4B').checked == false && boxChecked == 'selectinlaid4B')
	{
		document.getElementById('selectorder4').checked = false;
	}
	if (document.getElementById('selectinlaid4A').checked == true && document.getElementById('selectinlaid4B').checked == true && (boxChecked == 'selectinlaid4A' || boxChecked == 'selectinlaid4B'))
	{
		document.getElementById('selectorder4').checked = true;
	}
	if (document.getElementById('selectorder4').checked == true)
	{
		document.getElementById('selectinlaid4A').checked = true;
		document.getElementById('selectinlaid4B').checked = true;
	}
	if (document.getElementById('selectorder8').checked == false && boxChecked == 'selectorder8')
	{
		document.getElementById('selectinlaid8').checked = false;
		document.getElementById('selectcenter8').checked = false;
	}
	if (document.getElementById('selectorder8').checked == true && document.getElementById('selectinlaid8').checked == false && document.getElementById('selectcenter8').checked == true && boxChecked == 'selectinlaid8')
	{
		document.getElementById('selectorder8').checked = false;
	}
	if (document.getElementById('selectorder8').checked == true && document.getElementById('selectinlaid8').checked == true && document.getElementById('selectcenter8').checked == false && boxChecked == 'selectcenter8')
	{
		document.getElementById('selectorder8').checked = false;
	}
	if (document.getElementById('selectinlaid8').checked == true && document.getElementById('selectcenter8').checked == true && (boxChecked == 'selectinlaid8' || boxChecked == 'selectcenter8'))
	{
		document.getElementById('selectorder8').checked = true;
	}
	if (document.getElementById('selectorder8').checked == true)
	{
		document.getElementById('selectinlaid8').checked = true;
		document.getElementById('selectcenter8').checked = true;
	}
	matrix = new Array();
	for (var index=0; index<featureList.length; index++)
	{
		enumerateChecked(featureList[index], index);
	}
	if (document.getElementById('selectinlaid4A').checked == false && document.getElementById('selectinlaid4B').checked == true)
	{
		matrix.push(12);
		for (var index=0; index<featureList.length; index++)
		{
			checkableFeatures(featureList[index],index);
		}
		matrix.pop;
	}
	else
	{
		for (var index=0; index<featureList.length; index++)
		{
			checkableFeatures(featureList[index],index);
		}
	}
	if (document.getElementById('selectinlaid4A').disabled == true)
	{
		document.getElementById('selectinlaid4B').disabled = true;
	}
	baseSquareSet = new Array();
	// gather valid base squares into baseSquareSet
	for (var y=0; y<(baseSquares.length); y++)
	{
		var isValid = true;
		for (var x = 0; x < matrix.length; x++)
		{
			if (features[matrix[x]][y] == 0)
			{
				isValid = false;
				break;
			}
		}
		if (isValid)
		{
			baseSquareSet.push(baseSquares[y]);
		}
	}
	build();
}

//resets all buttons to unchecked and enabled
function reset()
{
	for (var index=0; index<featureList.length; index++)
	{
		enableBox(featureList[index]);
	}
	for (var index=0; index<featureList.length; index++)
	{
		uncheckBox(featureList[index]);
	}
	matrix = new Array();
	baseSquareSet = baseSquares;
	build();
}
