//CHECKJS  C:\temp\sudoku\tabulate.js 11/6/2005 12:30:18 PM
//tabulate.js hansonr@stolaf.edu 7:02 AM 11/7/2005
// creates the 9x9 grid with marks and checks for solution completion using brute force (two solutions the same)
// I don't like the mixing of functionality here.
RefList=[]
BgColors=["#FFFFFF","#F0F0A0","#000000","#FF0000","#00FF00","#A0A0FF","#0000FF"]
BgColors=["white","beige","black"  ,"red"    ,"green"  ,"purple" ,"blue"  ,"white","beige"]
//                   --everyotherblock--onlyposs--redflag-----hint------parity - or + -------
//                     0         1          2         3         4         5         6

Msgs= []

thisviewmode="sudoku"
thisLogMessage = ""
kgreen = -1
haveopencells=0
autoTitle = ""
autoPrefix="ex"
ishowallmarks=true
NUM9x9SPACE=1
NUM9x9LINES=2
NUMLINEARP=3
NUMLINEAR0=4
NUM9x9=5
NUMTABLE=6
NUMBEST=7
NUMCHAINTABLE=8
NUMSDK=9
NUMMARKS=10
NUMMARKSFIXED=11
NUMMARKSENCODED=12

MethodRefs = {}
TableRef=[]
MethodExamples = {}

function addAutoCode(M,m){
	return (M?(M.B?"B": " ")+(M.A1g||M.A1w||M.A1s||M.A1m?"A":" ")+(M.A2?"a":" ")+(M.W2||M.W3||M.W4?"W"+M.winglevel:"  ")+(M.Ms||M.Mw?m+(M.Mw?"w":"s"):"  ")+(M.H?"h":" ")+(M.P?"P"+(M.logiclevel>1?"++":M.logiclevel>0?"+ ":"  "):"   "):"      ")
}

function addAutoMsg(type,s){
	if(type != "" && !MethodsUsed[isautorun])addAutoMsg("")
	var M=MethodsUsed[isautorun]
	var icount=0
	var m=(isjustycycles?"Y":ijustcolor?"X":"M")
	if(type==""){
		LogicSave=[]
		MethodRefs = {}
		M=MethodsUsed[isautorun]={}
		M.previousRef = ""
		M.haveALS=M.B=M.Sn1=M.Sh1=M.Sn2=M.Sh2=M.Sn3=M.Sh3=M.Sn4=M.Sh4=M.W2=M.W3=M.W4=0 
		M.A1g = M.A1s = M.A1m = M.A1w = M.A2 = M.L = M.M = M.Ms = M.H = M.Mw = M.P = M.W = M.n = 0
		M.logiclevel=M.winglevel=M.tidbits=Logic_level=Wing_level=tidbits=0
		M.nclues=ncellssolved
		M.tidbits=countTidBits()
		return ""
	}else if(type=="?"){
		return addAutoCode(M,m).replace(/ /g,"")
	}else if(type=="-"){
		var id=(s?s:tableid)
		var n
		s="\n<pre>"
		s+="\npuzzle #clues #tidbits methods      #B   #A   #W   #"+m+" #H  #P  #steps"
		s+=    "\n--------------------------------------------------------------------"
		for(var i=1;i<=isautorun;i++)if(MethodsUsed[i]){
			M=MethodsUsed[i]
			s+="\n"+rightJust("0000"+i,5)+id+rightJust(M.nclues,6)+rightJust(M.tidbits,9)+"  "
			+addAutoCode(M,m)
			+rightJust(M.B?M.B:" ",5)
			+rightJust((n=M.A1g + M.A1w + M.A1m + M.A1s + M.A2)?n:" ",5)
			+rightJust((n=M.W2+M.W3+M.W4)?n:" ",5)
			+rightJust(M.Ms+M.Mw?M.Ms+M.Mw:" ",5)
			+rightJust(M.H?M.H:" ",2)
			+rightJust(M.P?M.P:" ",5)
			+rightJust(M.n,7)
			if(M.n)icount++
		}
		s+="\n</pre><br />Solved "+icount+"/"+isautorun+" "+(new Date())+"<br />"
		var A = []
		for (var type in MethodExamples)if ("Sh1;Sn1;".indexOf(type)< 0)A.push(type)
		A = A.sort()
		for (var i = 0; i < A.length; i++)
			s+="<br />\n"+MethodExamples[A[i]].join("<br />\n")+"\n<br />\n<br />\n"
		return s
	}else if(type=="="){
		s = "<b>Sudoku Difficulty Analysis:</b>\n<pre>"
			+"\n number of clues: "+M.nclues
			+"\n number of tidbits: "+M.tidbits
			+"\n number of steps required: " + M.n
			+ "\n number of eliminations from..."
			+ mLine(M,"scanning", "hidden singles", "Sh1", 1)
			+ mLine(M,"scanning", "naked singles", "Sn1", 1)
			+ mLine(M,"subsets", "hidden pairs", "Sh2")
			+ mLine(M,"subsets", "hidden tuples", "Sh3")
			+ mLine(M,"subsets", "hidden quads", "Sh4")
			+ mLine(M,"subsets", "naked pairs", "Sn2")
			+ mLine(M,"subsets", "naked tuples", "Sn3")
			+ mLine(M,"subsets", "naked quads", "Sn4")
			+ mLine(M,"locked", "locked candidates", "L")
			+ mLine(M,"grids", "X-wings", "W2")
			+ mLine(M,"grids", "swordfish", "W3")
			+ mLine(M,"grids", "4x4 grids", "W4")
			+ mLine(M,"almost", "singly-linked almost-locked sets", "A1w")
			+ mLine(M,"almost", "doubly-linked almost-locked sets", "A1m")
			+ mLine(M,"almost", "grid-based almost-locked sets (sashimi)", "A1s")
			+ mLine(M,"almost", "grid-based almost-locked sets (other)", "A1g")
			+ mLine(M,"medusa", "strong Medusa cycles", "Ms")
			+ mLine(M,"medusa", "weak Medusa cycles", "Mw")
			+ mLine(M,"almost", "Medusa with almost-locked sets", "A2")
			+ mLine(M,"hypothesis", "hypothesis and proof", "P")
			+"</pre>"
		thisLogMessage = s
		return s
	}

	if(ishint)return 1
	var ref = autoPrefix + "." + isautorun+"."+nsteps
	AutoMsgs.push("\n"+ref+" <b>"+type+": "+s+"</b><br />")
	if (M.previousRef != ref && lastmsg != "") {
		M.previousRef = ref
		if (SnapShot[solving]) {
			AutoMsgs = []
			var isALS = (type.charAt(0) == "A" || type == "B")
			if(M.haveALS || isALS == (igetalmostlockedx || igetalmostlockedy || igetalmostlockedb)) {
				if (!MethodExamples[type])MethodExamples[type] = []
				MethodExamples[type].push(["<!-- TYPE -->" + type + "<!-- ENDTYPE -->",
				"<a class=puzzle target=_blank href='index.htm?puzzle=" + ref + "&SHH" 
					+ (isALS||M.haveALS ? "ALS&" : "") 
					+ "SHH&HINT&MARKS=" + compressMarks(SnapShot[solving]) + "'"
					, "title=\""+lastmsg+"\""
					, ">"+ref + "</a>"
					, "\n<!-- ANS -->"+type,lastmsg,"<!-- ENDANS -->"
					, (redlist?"<!-- REDLIST -->"+redlist+"<!-- ENDREDLIST -->":"")
					, (redlistk?"<!-- REDLISTK -->"+redlistk+"<!-- ENDREDLISTK -->":"")
					, (greenlist?"\n<!-- GREENLIST -->"+type + " " + greenlist+"<!-- ENDGREENLIST -->":"")
					, (bluelist?"<!-- BLUELIST -->"+bluelist+"<!-- ENDBLUELIST -->":"")
					, (ishowchain>0?"<!-- CHAIN1 -->" + getChainListing(ishowchain)+"<!-- ENDCHAIN1 -->":"")
					, (ishowchain2>0?"<!-- CHAIN2 -->" + getChainListing(ishowchain2)+"<!-- ENDCHAIN2 -->":"")
					].join(" "))
				//if(isALS)M.haveALS=true
			}
		}
		if (!MethodRefs[type])MethodRefs[type] = solving
		MethodsUsed[isautorun][type]++
	}
	MethodsUsed[isautorun].logiclevel=Logic_level
	MethodsUsed[isautorun].winglevel=Wing_level
	return "1"
}

function mLine(M,ref,name,type, noExample){
	return "\n   ...<a target=_blank href=explain.htm#"+ref+">"+name+"</a>: " 
		+ M[type]+(M[type] > 0 && MethodRefs[type] && !noExample? getMarkLink("(step "+MethodRefs[type]+")",SnapShot[MethodRefs[type]], "DOSTEP=1", 1,"popup.htm") : "")
}

function compressMarks(T){
	var s=""
	for(var i=0;i<9;i++)for(var j=0;j<9;j++){
		s+=","
		for(var k=0;k<9;k++)if(T[k+j*9+i*81])s+=(k+1)
	}
	return s.substring(1)
}

var ishowcolors = true
var weakmarks = ""
function createTable(imode, fullchainlist){
	MouseColor = {}
	fullchainlist || (fullchainlist="")
	var igofast=doGoFast()
	if(!imode)imode=0
	var issnap=(imode<0?0:imode)
	onlythisn=(imode<0?-imode:0)
	if (!onlythisn)showChainDiv(-1)
	if(issnap==1){
		saveRef("createtable")
		if (getValues(0))return false // user indicated -n -- so show marks
	}
	theref=""
	chainlist=(ishowchain > 0 ?getChainListing(ishowchain): ishowchain < 0 ? alsGetChainListing(-ishowchain) : "")
	chainlist2=(ishowchain2 > 0 ?getChainListing(ishowchain2): ishowchain2 < 0 ? alsGetChainListing(-ishowchain2) : "")
	weakmarks = (idontshowchain?getAssociatedNodeSet(idontshowchain):yellowlist != "" ? weakmarks : "")
	ishowcolors=(greenlist!="NOSHOW")
 	showChainDiv(-1)
	ihavemarks=(issnap == 3 || !issnap)
	if(thisviewmode=="jmol")updateAllowed(1)
	if (lastmsg.indexOf(" via ALS")>=0)bluelist += lastmsg.substring(lastmsg.indexOf(" via ALS"))
	isbad=0
	var s=(enableUserEntry || onlythisn ? "<div id=infodiv class=info>"+(onlythisn ? getRowColumnLabels(onlythisn, 0) 
		: ihavemarks?"click on a mark to set it; off the marks to set several at once"
		: "click on a cell to set one or more values")+"</div>" 
	  : "")
	s+="<table border='0' cellspacing='0' cellpadding='0'><tr><td>"
	+"<table id='outertable' border='5' cellspacing='0' cellpadding='0'><tr><td style='background-color:#a0a0a0'>"
	+"<table border='0' cellspacing='0' cellpadding='0' style='background-color:#a0a0a0'>"
	var nok=0

	haveopencells=0
	for(var k=0;k<9;k++){ //blocks
		var i=Math.floor(k/3)*3
		var j=(k%3)*3
		if(k%3==0)s+="<tr>"
		s+="<td valign='center' align='center'><table class='block' border='0' cellspacing='0' cellpadding='0'>"
		for(var n=0;n<9;n++){
			if(n==3 || n==6){
				i++
				j=(k%3)*3
			}
			if(n%3==0)s+="<tr height='53'>"
			s+=createTableEntry(i,j,issnap)
			if(Data[i][j].N && Data[i][j].isOK)nok++
			j++
			if(n%3==2)s+="</tr>"
		}
		s+="</table></td>"
		if(k%3==2)s+="</tr>"
	}
	weakmarks=""
	s+="</table></td></tr></table></td><td bgcolor=white>&nbsp;&nbsp;&nbsp;&nbsp;</td></tr></table>"

	if (thisviewmode=="sudoku" && (iShowAnswers || !solving) && !haveopencells) {
		showChainDiv(Math.max(ishowchain, 0), Math.max(ishowchain2, 0), 0,fullchainlist)
	}
	ishowchain = ishowchain2 = 0
	if (iShowAnswers)showRef()

	if(solving)solving++
	if(!solving || solving>MAXSOLVERSTEPS || isbad || isdone && iShowAnswers || s!=lastdisplay && !igofast || isstuck && solving) {
		setSudoku(s)
	}
	if(thisviewmode=="input" && (!solving || enableUserEntry))showDataBlock()

	var fullmsg = ""
	if(isdone || isbad || isstuck && solving) {
		fullmsg = FullMsgs.join("<hr />")
		FullMsgs = []
	}
	if(isstuck && solving){
		solving=0
		isstuck=0
		nsteps=0
		s=" STUCK"+getRef(1,isautorun?NUMLINEARP:0,1)
		fullmsg=s+fullmsg
		logMessage(fullmsg,1)
		if(isautorun){
			AutoMsgs.push(s)
			setTimeout("doAutoRun(isautorun)",1000)
			return true
		}
		if(icheckgrids && icheckmedusa && icheckweaklinks && (icheckreverselogic||icheckforwardlogic) && ichecklogicsubsets){
			alert("The solver is stuck. This puzzle has either no UNIQUE solution or requires more depth in terms of trial and error.")
		}else{
			alert("The solver is stuck. Have you enabled all the options? Add an option and then click 'Help'.")
		}
		updateAllowed(1)
		getStrongChains(0)
		return true
	}

	lastdisplay=s

	if(isdone){
		if(!MethodsUsed[isautorun])addAutoMsg("")
		MethodsUsed[isautorun].n=nsteps
		s=(!iShowAnswers && !ishowdetail 
			? addAutoMsg("=") 
			: "<br />" + (isautorun?"#"+isautorun+" ":"")+"Done!"+(solving?" ("+nsteps+" steps)":"")+" "+addAutoMsg("?")
			)
		if(!solving)
			fullmsg=logGetMessage()
		else if(!iShowAnswers && !ishowdetail)
			fullmsg=""
		
		var wasSolving = solving
		solving=0
		nsteps=0
		if (iShowAnswers || ishowdetail)s+=getRef(1,isautorun?NUMLINEARP:0,1)
		if(isautorun)s+=getRef(1,NUM9x9LINES,1)
		fullmsg=s+fullmsg	//;fullmsg=fullmsg.replace(/\</g,"<br>&lt;"
		logMessage(fullmsg,1)
		if(isautorun){
			nautosolved++
			AutoMsgs.push(s)
			document.title=autoPrefix + ": SOLVED "+nautosolved+"/"+nautorun
			setTimeout("doAutoRun(isautorun)",1000)
			return true
		}
		isDone(wasSolving)
	}else if(isbad){
		solving=0
		isautorun=0
		alert("This puzzle appears to have a mistake in it. Please let me know if you think it does not.")
		logAddMessage(AutoMsgs.join(""))
		logMessage(fullmsg+logGetMessage(),1)
	}else if(ishint==1){
		solving=0
	}else if(solving>MAXSOLVERSTEPS && !isautorun){
		solving=0
		alert("Press 'Solve' again to continue.")
	}else if(solving){
		document.title="Step "+solving+" "+addAutoMsg("?")+(isautorun?" (puzzle #"+nautorun+" -- SOLVED "+nautosolved+"/"+(nautorun-1)+")":"")
	}
	if(!solving)document.title=pageTitle

	if(thisviewmode=="jmol")create3DModel(thisslicen,thisr_or_c)
	return true
}

function createTableEntry(i,j,issnap){
	var n=0
	var sn=""
	var coord=""
	var s=""
	var C=Data[i][j]
	var thestyle="style='background-color:"+BgColors[C.block%2]+"'"
	var thehint=""
	var ishowmarks=0
	var kg = -1
	if(idonotstop){
		ishowchain=ishowchain2=0
		greenlist=bluelist=yellowlist=""
	}
	var colorlist=greenlist+bluelist+redlist+yellowlist
	var scol = ""
	s+="<table width='49' border='0' cellspacing='0' cellpadding='0' class='cell'>" //4*9+3*5=51 +2
	s+="<tr height='49'><td "+thestyle+" width='49'>"
	s+="<table id='c"+i+"_"+j+"' width='48' border='0' cellspacing='3' cellpadding='0'>"
	if(ihavechecked && !C.N && !C.isOK){
		s+="<tr><th class='bad' title='no value is possible here'>X</th></tr>"
		isbad=1
	}else if(C.isopen){
		haveopencells=1
		s+="<tr><th>"+"<input id='cell"+i+j
		  +"' style='width:30px' type='text' value='"
		  +(isdemo?xStr(Possible.XCell[i][j]):marksForCell(i,j,1))+"' />"+"</th></tr>"
	}else if(!C.isOK && C.N){
		s+="<tr><th class='bad' title='this value is not possible'>"+C.N+"</th></tr>"
		isbad=1
	}else if(C.fixed || issnap && C.N){
		s+="<tr><th class='"+(issnap?"snap":"fixed")+"' title='fixed'>"+(!onlythisn||C.N==onlythisn?C.N:"")+"</th></tr>"
	}else if(C.N){
		s+="<tr><td valign=center align=center>"+(isdemo?"":!onlythisn||C.N==onlythisn?C.N:"")+"</td></tr>"
	}else if(!issnap && ishowcolors && (C.onlypossible || colorlist.indexOf(C.showinfo)>=0)){
		scol = (C.onlypossible || redlist.indexOf(C.showinfo)>=0?"red":bluelist.indexOf(C.showinfo)>=0?"blue"
			:greenlist.indexOf(C.showinfo)>=0?"green" : "yellow")
		if(!onlythisn)thestyle="style='background-color:"+scol+"'"
		ishowmarks=1
		if(!onlythisn && scol=="green")kg=kgreen
		if(ishint)ishint++
	}else if(ishint && !C.onlypossible || ishint==1){
		if (ishowallmarks)ishowmarks=1
		if(C.onlypossible || greenlist.indexOf(C.showinfo)>=0){
			if(!onlythisn)thestyle="style='background-color:red'"
			ishowmarks=1
		}else if(redlist.indexOf(C.showinfo)>=0){
			if(!onlythisn)thestyle="style='background-color:red'"
			ishowmarks=1
		}else{
			s+="<tr><td></td></tr>"
		}
		if(C.onlypossible){
			thehint="the number in this cell must be "+C.onlypossible
			if(!onlythisn)thestyle="style='background-color:red'"
			ishowmarks=1
			ishint++
		}

	}else if(issnap == 1 || issnap == 2 || ishint>1){
		s+="<tr><td></td></tr>"
	}else{
		ishowmarks=1
	}
	if(ishowmarks){
		var p = 0
		var nmarks = 0
		for(var k=0;k<9;k++){
			n=k+1
			sn=""+n
			var kclass=""
			var ktitle=""
			coord=coordOf(C,k)
			if(k%3==0)s+="<tr height='12'>"
			if(!onlythisn && ishowcolors && (C.Eliminated[k]||C.Impossible[k]) && C.AllowedByCross[k]){
			//if(ishowcolors && C.Impossible[k] && C.AllowedByCross[k]){
				//st+=","+n
				kclass=BgColors[onlythisn?C.block%2:4]
				ktitle=n+" is not valid here "+C["impossibleType"+k]
				if(!onlythisn){
					thestyle="style='background-color:red'"
					kclass="green"
				}
				if(idonotstop)C.Eliminated[k]=0
			}else if(C.Allowed[k]){
				nmarks++
				//st+=","+n
				if(ishowcolors && n==onlythisn)thestyle="style='background-color:red'"
				if(ishowchain > 0 && chainlist.indexOf(coord)>=0 && C.Parity[ishowchain]){
					kclass=BgColors[C.Parity[ishowchain][k][0]>0?6:5]
					thestyle="style='background-color:"+(n==onlythisn?"violet":chainlist.indexOf(coord+"-")>=0||chainlist.indexOf(coord+"}")>=0?"#000080":"blue")+"'"
				} else 	if(ishowchain < 0 && chainlist.indexOf(coord)>=0){
					kclass=BgColors[6]
					thestyle="style='background-color:green'"
				} else 	if(ishowchain2 < 0 && chainlist2.indexOf(coord)>=0){
					kclass=BgColors[6]
					thestyle="style='background-color:teal'"
				}else if(ishowcolors && ishowchain2>0 && chainlist2.indexOf(coord)>=0 && C.Parity[ishowchain2]){
					kclass=BgColors[C.Parity[ishowchain2][k][0]>0?6:5]
					thestyle="style='background-color:teal'"
				}else if(ishowcolors && pointlist.indexOf(coord)>=0){
					kclass="red"
				}else if(ishowcolors && kg==k){
					kclass="redgreen"
				}else{
					var c = (n==onlythisn?2:!onlythisn && C.onlypossible==n?3:!onlythisn?2:C.block%2)
					kclass=BgColors[c]
					if (c < 2)sn="&nbsp;"
				}
				ktitle=(C.onlypossible==n?"must be "+n:"set to "+n)
				if (C.Parity){
					if(ishowchain > 0 && C.Parity[ishowchain]&&C.Parity[ishowchain][k])
						ktitle += " node:"+coord + " chain "+nodeData(C.Parity[ishowchain][k][1],FULLCHAINCODE)
					else if (ishowchain2 > 0 && C.Parity[ishowchain2]&&C.Parity[ishowchain2][k])
						ktitle += " node:"+coord + " chain "+nodeData(C.Parity[ishowchain2][k][1],FULLCHAINCODE)
				}
			} else {
				n=0
			}
			if (n==0) {
				sn="&nbsp;"
				kclass=BgColors[C.block%2]
			} else 	if ((p=weakmarks.indexOf(coord)) >= 0) {
				kclass="weak" + weakmarks.charAt(p+6)
			}
			var ncid = "nc_"+i+"_"+j+"__"+n
			s+="<td width='12' align='center' valign='center' id='"
				+ncid+"' onmouseover=\"doMouseIn('"
				+ncid+"',1)\" onmouseout=\"doMouseIn('"
				+ncid+"',0)\" class='k"+kclass +"' onclick=\"doClick("+i+","+j +","+n+")\""
				+(ktitle ? " title=\""+ktitle + "\"":"") + ">" + sn+"</td>"
			if(k%3==2)s+="</tr>"
		}		
	}
	if(thestyle.indexOf(":red")>=0)redlist+=C.showinfo  //update for hint
	s+="</table></td></tr></table>"
	s="<span onclick='doClick("+i+","+j+")' title='click here to change possibilities'>"+s+"</span>"
	s="<td align='center' valign='center' width='52' class='cblock"+(C.block%2)+"' "+thestyle+"'>"+s+"</td>"
	s=s.replace(/THESTYLE/,"")
	return s//.replace(/\</g,"&lt;")
}

thismouseid = ""
MouseColor = {}
function doMouseIn(id,isin) {
	if (id.indexOf("__0")>=0)return
	var d = document.getElementById(id)
	if (!d || document.getElementById("radtag0").checked)return
	if (isin) {
		if (!MouseColor[id])MouseColor[id] = [d.style.backgroundColor,0]
		thismouseid = id
		setTimeout("colorCell('" + id + "')",1000)
	} else {
		thismouseid = ""
	}
}

function colorCell(id) {
		if (thismouseid != id || document.getElementById("radtag0").checked)return
		var d = document.getElementById(id)
		var color = MouseColor[id]
		d.style.backgroundColor = (color[1] ? color[0] : document.getElementById("radtag1").checked ? "yellow" : "purple")
		color[1] = 1-color[1]
}

function clearMouseColors() {
	for (id in MouseColor) {
		var d = document.getElementById(id)
		var color = MouseColor[id]
		if (color[1] && d){
			d.style.backgroundColor = color[0]
			color[1] = 1-color[1]
		}
	}
}

function setMouseColor(isSet) {
	if (document.getElementById("radtag0").checked) {
		clearMouseColor()
		return
	}
	for (id in MouseColor) {
		var d = document.getElementById(id)
		var color = MouseColor[id]
		if (!d)continue
		if (d.style.backgroundColor == (document.getElementById("radtag1").checked ? "yellow" : "purple")){
			d.style.backgroundColor = color[0]
			color[1] = 1-color[1]
			var S = id.split("_")
			var i = parseInt(S[1])
			var j = parseInt(S[2])
			var n = parseInt(S[4])
			var P
			if (isSet) {
				Data[i][j].N=n
			} else if (xNum(P=Possible.XCell[i][j])==2) {
				n = xFirst(P & ~Pwr2[n-1]) + 1 
				Data[i][j].N=n
			}
		}
	}
	createTable(1)
	getNextStep(0)
}

function getCheckBoxOptions() {
	s=""
	if (igetalmostlockedx && igetalmostlockedy && igetalmostlockedb) {
		s += "ALS&"
	} else {
		if (igetalmostlockedx)s+="ALSX"
		if (igetalmostlockedy)s+="ALSY"
		if (igetalmostlockedb)s+="ALSB"
	}
	s += (!isalsfull ? "&noalsfull&" : "")
	s += (isalsv2 ? "&alsv2&" : "")

	if(!idoglyphs)s+="&NOGLYPHS"
	s+= (isjustycycles?"YCYCLESONLY&":ijustcolor?"COLORONLY&":"")
	return (s ? "SHH" + s : "")
}

function getMarkLink(text,T,options, asJS, base){
	var t = getCompressedMarks(T);
	if (!t)return ""
	var s = (base? base : baseRef)+"?" + getCheckBoxOptions()+t+(options?"&"+options : "")

	if(asJS)
		s="\"javascript:docOpen('"+s+"')\""
	else
		s="\""+s+"\"  target=_blank"
	return "<a href=" + s + ">"+text+"</a>"
}

function getCompressedMarks(T) {
	if(!T){
		if (TableRef.length){
			TableRef=[]
			getTableRef(TableRef)
		}
		T=TableRef
	}
	var t=compressMarks(T)
	return (t ? "MARKS="+t.replace(/\[/g,".").replace(/\]/g,";") : "")
}

function getNumericalTable(S,ch0,ch1,coord,isplain){
	var s=""
	var st=""
	var row1=""
	var row2=""
	var ch=""
	var n=0
	var T=[[],[],[],[],[],[],[],[],[]]
	var C=[5,5,5,5,5,5,5,5,5]
	var r=0
	var ch2="xxx"
	var ischaintable=0
	for(var i=0;i<9;i++){
		for(var j=0;j<9;j++){
			row1=""
			row2=""
			for(var k=0;k<9;k++){
				ch=S[k+j*9+i*81]
				if(!ch)ch=""
				if(ch==""){
				}else if("123456789".indexOf(ch)>=0){
					row1+=ch
					row2+=" "
				}else{
					row1+=(k+1)
					if(coordOf(Data[i][j],k)==coord){
						ch2=ch
						ch="*"
					}
					row2+=ch
					ischaintable=1
				}
			}
			if(row1.length>C[j])C[j]=row1.length
			T[i][j]=[row1,row2]
		}
	}
	for(var j=0;j<9;j++){
		st+="+-----------------".substring(0,C[j]+3)
		if(j==2||j==5)st+="+"
	}
	s="   "+st
	for(var j=0;j<9;j++)s=s.replace(/\+\-\-\-\-\-/,"|---%"+(j+1))
	s=s.replace(/\+/g,"|")
	st=(isplain?"\n":" @")+"---"+st.replace(/\+\+/g,"||")
	s+=st.replace(/\||\+/g,"-")
	for(var i=0;i<9;i++){
		s+=" @&"+(i+1)+" | "
		for(var j=0;j<9;j++){
			s+="         ".substring(0,C[j]-T[i][j][0].length)+T[i][j][0]
			s+=(j==2||j==5?" || ":j==8?"":" | ")
		}
		if(ischaintable){
			s+=" @   | "
			for(var j=0;j<9;j++){
				s+="         ".substring(0,C[j]-T[i][j][1].length)+T[i][j][1]
				s+=(j==2||j==5?" || ":j==8?"":" | ")
			}
		}
		s+=(i==2||i==5?st.replace(/\-|\+/g,"="):i==8?st.replace(/\||\+|\-/g,"."):st)
	}
	if(ch0){
		r=new RegExp(ch0,"g")
		s=s.replace(r,"!")
	}
	if(ch1){
		r=new RegExp(ch1,"g")
		s=s.replace(r,"#")
	}
	if(ch0)s=s.replace(/\!/g,"<b><font color=blue>"+ch0+"</font></b>")	
	if(ch1)s=s.replace(/\#/g,"<b><font color=red>"+ch1+"</font></b>")	
	if(ch2)s=s.replace(/\*/,"<b><font color=red>"+ch2+"</font></b>")
	s=s.replace(/\&/g,"r")
	s=s.replace(/\%/g,"c")
	s=s.replace(/\@/g,"\n")
	if(!isplain)s=s.replace(/(\ \d\ )/g,"<b><font color=red>$1</font></b>")
	return s
}

function getPuzzleFromMarks(T){
	var s=""
	var sout=""
	for(var i=0;i<9;i++)for(var j=0;j<9;j++){
		s=""
		var isneg=false
		for(var k=0;k<9;k++){
			var n=T[k+j*9+i*81]
			if(n)s+=(k+1)
			if(n<0)isneg=true
		}
		if(s.length==1)sout+=(isneg?"-":"")+s
		sout+=","
	}
	return sout.substring(0,sout.length-1)
}


function getRef(isformatted,nformat,ishtml){
	if(!nformat)nformat=numberformat
	var Ref=[]
	var s=""
	var scell=""
	var isinit=1
	if(!ishtml)ishtml=0
	if(isdemo){
		for(var i=0;i<9;i++)for(var j=0;j<9;j++){
			scell="["+(i+1)+","+(j+1)+":"
			if(Data[i][j].N){
				s+=scell+Data[i][j].N+"]"
			}else{
				for(var k=0;k<9;k++){
					if(Data[i][j].Allowed[k])scell+=","+(k+1)
				}
				scell=scell.replace(/\:\,/g,":")+"]"
				if(scell.indexOf(":]")<0)s+=scell
			}
		}
		return ["DEMO="+s]
	}else if(isformatted){
		if(nformat==NUMMARKSENCODED){
			return encodeMarks(getTableRef(Ref))
		}
		if(nformat==NUMSDK){
			if(!ihavemarks)showMarks(true)
			s+="[Puzzle]\n"
			for(var i=0;i<9;i++){
				for(var j=0;j<9;j++){
					s+=(Data[i][j].N && Data[i][j].fixed?Data[i][j].N:".")
				}
				s+="\n"
			}
			s+="[State]\n"
			for(var i=0;i<9;i++){
				for(var j=0;j<9;j++){
					s+=(Data[i][j].N?Data[i][j].N:".")
				}
				s+="\n"
			}
			s+="[PencilMarks]\n"
			for(var i=0;i<9;i++){
				for(var j=0;j<9;j++){
					s+=(j==0?"":",")
					if(!Data[i][j].N)
						for(var k=0;k<9;k++)
							if (Data[i][j].Allowed[k])s+=(k+1)
				}
				s+="\n"
			}
			return s
		}
		if(nformat==NUMMARKS){
			s=""
			for(var i=0;i<9;i++)for(var j=0;j<9;j++)s+="," + marksForCell(i,j,1)
			return s.substring(1)
		}
		if(nformat==NUMMARKSFIXED){
			s=""
			for(var i=0;i<9;i++)for(var j=0;j<9;j++)s+="," + marksForCell(i,j,-1)
			return s.substring(1)
		}

		if(nformat==NUMTABLE||nformat==NUMCHAINTABLE){
			if(!ihavemarks)showMarks(true)
			getTableRef(Ref,nformat==NUMCHAINTABLE)
			s=getNumericalTable(Ref,0,0,0,!ishtml)
			s=(!ishtml||nformat!=NUMCHAINTABLE?"":StrongChains.length<2?"<h3>There are no strong chains.</h3>":"<h3>Medusa Chain Compatibility Analysis<br><br>"+(StrongChains.length-1)+" strong chains are labeled A-"+ALPHABET.charAt((StrongChains.length-2)%26)+", with alternating parity shown using lowercase.</h3>")+s
			if(ishtml)s="<pre>"+s+"</pre>"+(ishowtimings?(new Date()):"")+"<br />"
			return s
		}
		for(var i=0;i<9;i++){
			for(var j=0;j<9;j++){
				s+=(Data[i][j].N?Data[i][j].N:".")+(j==2||j==5?" ":"")
				if(Data[i][j].N && !Data[i][j].fixed)isinit=0
			}
			s+="\n"
			if(i==2||i==5)s+="\n"
		}
		if(nformat==NUMBEST && !isinit)return getRef(0,0,0).join(",")
		if(nformat==NUM9x9SPACE){
		}else if(nformat==NUM9x9){
			s=s.replace(/ /g,"").replace(/\n\n/g,"\n")
		}else if(nformat==NUM9x9LINES){
			//s="---+---+---\n"+
			s=s.replace(/\n\n/g,"\n---+---+---\n").replace(/ /g,"|")
			//+"---+---+---"
		}else if(nformat==NUMLINEARP){
			s=s.replace(/\s/g,"")
		}else if(nformat==NUMLINEAR0||nformat==NUMBEST){
			s=s.replace(/\s/g,"").replace(/\./g,"0")
		}
		if(ishtml)s="<pre>"+s+"</pre>"+(ishowtimings?(new Date()):"")+"<br />"
		return s
	}

	for(var i=0;i<9;i++)for(var j=0;j<9;j++){
		Ref[Ref.length]=(Data[i][j].fixed?-Data[i][j].N:Data[i][j].N?Data[i][j].N:"")
	}

	return Ref
}

function marksForCell(i,j,negateFixed){
	if(Data[i][j].fixed)return Data[i][j].N * negateFixed
	if(Data[i][j].N)return Data[i][j].N
	var s=""
	for(var k=0;k<9;k++)if (Data[i][j].Allowed[k])s+=(k+1)
	if(s=="123456789")s=""
	return s
}

function getTableRef(T,ischaintable,excludeimpossible){
	var ch=""
	var ichain=0
	for(var i=0;i<9;i++)for(var j=0;j<9;j++)for( var k=0;k<9;k++){
		if(Data[i][j].Allowed[k] && (excludeimpossible || (Possible.XCell[i][j]&Pwr2[k]) && !Data[i][j].Impossible[k]) && !T[k+j*9+i*81]){
			ch=k+1
			if(ischaintable&&!Data[i][j].N&&chainOf(i,j,k)){
				ichain=chainOf(i,j,k)
				if(Data[i][j].Parity[ichain])ch=chainCode(ichain,Data[i][j].Parity[ichain][k][0])
			}
			if(Data[i][j].N && (k+1)!=Data[i][j].N)ch=""
			T[k+j*9+i*81]=ch
		}
	}
	return T
}

function getValues(ifix){
	var isopen=false
	if(ifix)for(var i=0;i<9;i++)for(var j=0;j<9;j++)if(Data[i][j].N)Data[i][j].fixed=1
	for(var i=0;i<9;i++)for(var j=0;j<9;j++)if(Data[i][j].isopen){
		isopen=true
		var v=getSudokuValue(i, j);
		var val=parseInt(v);
		if(isNaN(val))val=0
		if(val>9){
			var s=""
			for(var k=0;k<v.length;k++)s+=","+v.charAt(k)
			v=s.substring(1)
		}
		if(v.indexOf(",")>=0||v==""&&isdemo){  //for demos only
			var S=v.split(",")
			Data[i][j].Allowed=[0,0,0,0,0,0,0,0,0]
			for(var p=0;p<S.length;p++){
				var v=parseInt(S[p])
				if (v>0 && v<=9)
					Data[i][j].Allowed[v-1]=v
			}
			if (!isdemo){
				for(var p=0;p<9;p++)
					if (!Data[i][j].Allowed[p])userdisallowslist+=coordOf(Data[i][j],p)
			}
		}else{
			v=val
			if(val<0 && ihaveset){
				val=-val
				if(val>9)val=0
				if(val){
					Data[i][j].Impossible[val-1]=val
					userdisallowslist+=coordOf(Data[i][j],val-1)
				}
			}else{
				if(val<0 || val>9)val=0
				Data[i][j].N=val
			}
			Data[i][j].fixed=(v!=0  && !ihaveset)
		}
		Data[i][j].isopen=0
	}
	return isopen && userdisallowslist!=""
}

function loadMarks(s, doclear){
	var T=uncompressMarks(s)
	LinearData=getPuzzleFromMarks(T).split(",")
	setData()
	setMarks(T)
	var x = 0
	userdisallowslist=""
	for(var i=0;i<9;i++)for(var j=0;j<9;j++)if(!Data[i][j].fixed)
		for(var k=0;k<9;k++)
			if(!Data[i][j].Allowed[k])userdisallowslist+=coordOf(Data[i][j],k)
	if(doclear)clearDivs()
	getNextStep(1,0,1,1)
	ihavechecked=1
}

function loadSnapShot(i){
	i = parseInt(""+i)
	if(!isNaN(i))loadMarks(compressMarks(SnapShot[i]),false)
	if(thisLogMessage!= "")logMessage(thisLogMessage)
}


function loadSelectBox(sblock,isarray,thisone){
	if (thisone && thisone.indexOf("?") == 0)thisone=thisone.substring(1)
	var n = 0;
	var sout="<a href=javascript:doLoadSelection()>Load Selected</a>"
	+"&nbsp;&nbsp;&nbsp;<a href=\"javascript:doLoadSelection();doSolve()\">Solve</a>"
	+"&nbsp;&nbsp;&nbsp;<a href=\"javascript:doLoadSelection();doLoadBlock(-1)\">Standard View</a>"
	+"&nbsp;&nbsp;&nbsp;<a href=\"javascript:doAutoRun(-1)\">Automatic Run</a>"
	+"&nbsp;&nbsp;&nbsp;<a href=\"javascript:doShowRunLog()\">Show Run Log</a>"
	+"&nbsp;&nbsp;&nbsp;<a href=\"javascript:void(istoprun=1)\">Stop Run</a>"
	+"<br><select id=selection size=5 onkeyup=\"setTimeout('doLoadSelection()',10)\">"
	if(isarray){
		for(var i=0;i<sblock.length;i++)if(sblock[i].length==81)sout+="<option>"+(++n)+": "+sblock[i].replace(/0/g,'.')+"</option>"
	}else{
		for(var i=0;i<sblock.length;i+=81)sout+="<option>"+(i/81+1)+": "+sblock.substring(i,i+81)+"</option>"
	}
	sout+="</select><br>"
	setSelectBox(sout);
	if (thisone)doLoadSelection(parseInt(thisone))
}

function saveRef(info){
	var T=[]
	getTableRef(T)
	var t=compressMarks(T)
	if(RefList.length==0||RefList[RefList.length-1]!=t)RefList[RefList.length]=t
	showStepSelect(t)
}

function showDemo(sdemo){
	var S=sdemo.replace(/\,\[/g,"[").replace(/(\]|\})/g,"").split("[")
	var C=[]
	var RC=[]
	var V=[]
	var D={}

	isdemo=1

	LinearData=new Array()
	setData()
	initPossible()

	for(var i=0;i<9;i++)for(var j=0;j<9;j++){
		Data[i][j].N=0
		Data[i][j].Allowed=[0,0,0,0,0,0,0,0,0]
	}
	for(var i=1;i<S.length;i++){
		C=S[i].split(":")
		RC=eval("["+C[0]+"]")
		V=eval("["+C[1]+"]")
		D=Data[RC[0]-1][RC[1]-1]
		D.N=(V.length==1?V[0]:0)
		if(D.N)D.fixed=1
		for(var j=0;j<V.length;j++)D.Allowed[V[j]-1]=V[j]
	}

	crossCheckAll()
	createTable(0)

}

function toAssistantMode(sblock,issolution){
	var sout=""
	var s=""
	if(!issolution){
		if(sblock.indexOf("http://") == 0 && sblock.indexOf("?") >= 0) {
			sblock = sblock.substring(sblock.indexOf("?") + 1)
			sblock = sblock.substring(sblock.indexOf("=") + 1).split("&")[0]
		}
		if(sblock.indexOf(".")>=0)sblock.replace(/\-\-\-\-\-\-\-\-\-\-\-\n/g,"")
		if(sblock.indexOf("-c1-")>=0||sblock.indexOf("----------------+----------------")>=0){  //table 	
			sblock=sblock.replace(/\*/g," ")
			sblock=sblock.replace(/\n/g," \n")
			sblock=sblock.replace(/0/g," 123456789 ")
			sblock=sblock.replace(/r\d/g,"").replace(/c\d/g,"").replace(/[a-z]|[A-Z]/g,"")
			sblock=sblock.replace(/ (\d+) /g," ,[$1] ")
			sblock=sblock.replace(/ (\d+) /g," ,[$1] ")//in case nnn mmm
			sblock=sblock.replace(/\](\d)/g,"],$1")
			sblock="["+sblock+"]"
			sblock=sblock.replace(/\.|\-|\+|\||\¦|\s|\=/g,"").replace(/\[\,/g,"[")
			return sblock
		}
		if(sblock.indexOf("----")>=0)sblock=sblock.replace(/(\-|\+)/g,"")
		if(sblock.indexOf("-+")>=0)sblock=sblock.replace(/(\-|\+)/g,"")
		sblock=sblock.replace(/(\-|\?|\.|\_)/g,"0")
		if(sblock.indexOf("\t")>=0){
			sblock=sblock.replace(/\t/g,"0")
		}
		if(sblock.indexOf(",")>=0)
			return "[" + sblock+"]"
	}
	sblock=sblock.replace(/\D/g,"")
	if(sblock.length>81 && sblock.length%81==0)loadSelectBox(sblock,0)

	for(var i=0;i<sblock.length;i++){
		s=sblock.charAt(i)
		if("1233456789".indexOf(s)>=0){
			sout+=s
			sout+=","
		}else{
			sout+=","
		}
	}
	return sout
}

function uncompressMarks(s){
	if (s.charAt(0) == "$")return uncompressMarks(decodeMarks(s))
	var S=[]
	var T=[]
	var s2=""
	s=s.replace(/\./g,"[").replace(/\;/g,"]").replace(/\](\d)/g,"],$1")
	if (s.indexOf("[") < 0)s="[" + s + "]"
	try{
		S=eval(s)
		for(var i=0;i<S.length;i++)if((typeof S[i] != "object") && !isNaN(S[i]) || (typeof S[i] == "object") && S[i][0] > 9){
			s=S[i]+""
			s2=""
			for(var j=0;j<s.length;j++)s2+=","+s.charAt(j)
			s2=s2.replace(/\-\,/g,"-")
			S[i]=eval("["+s2.substring(1,s2.length)+"]")
		}
		for(var i=0;i<S.length;i++)for(var j=0;j<9;j++)T[i*9+Math.abs(S[i][j])-1]=S[i][j]+""
		if (itransposemarks)T=transposeMarks(T)
	}
	catch(e){
		logMessage(s)
		alert("There was an error processing MARKS="+s)
	}
	return T
}

function transposeMarks(T) {
	var A = []
	for (var i = 0; i < 9; i++)
		for (var j = 0; j < 9; j++)
			for (var k = 0; k < 9; k++)
				A[j*81+i*9+k] = T[i*81+j*9+k]
	return A
}


function get729x324(){
	var coord=""
	var s=""
	var S=[]
	s+="<table width=600><tr><td><h3>Exact Cover Matrix</h3>"
	s+="This table connects all possible candidates (on the left) with the constraints they correspond to (on the right). An actual exact cover matrix is a binary matrix -- all 1s and 0s. This version uses digits 1-9 simply for easier reading. All 9x9x9=729 candidate possibilities are listed along with all 9x9x4=324 constraints. Because we have some clues, some of these candidates are no longer possible -- they have no corresponding constraints. "
	s+="The <b>exact cover</b> problem amounts to finding the set of candidate-rows that \"exactly covers\" the columns with one value per column. The \"cand/N\" line counts the number of marks in each column. When this line reads all 1s, we are done. (If you solve the puzzle, you will see that this is the case.) "
	s+="<br><br>Sudoku Assistant, as all solvers do, solves this problem by investigating what the logical consequenses are of selecting various subsets of candidate-rows. A naked pair -- four specific candidate-rows--for example, might require that certain columns have a mark in them. That would eliminate any candidates from the solution that would require additional marks there. "
	s+="The essence of the problem is to eliminate candidate-rows until just the \"right\" rows remain. To see how this works, check out these <a target=_blank href=exactcover.htm>exact cover examples</a>."
	s+="<br><br>3D Medusa, as all techniques, follows these connections to relatively simple logical inconsistencies. Hypothesis and (dis)proof goes further, looking for inconsistencies deeper along the solution. "
	s+="<br><br>An alternative, more efficient algorithm, the <b>Dancing Links Algorithm (DLX)</b>, uses a clever and very efficient computational process of reversibly doubly-linked data structures to explore <i>all</i> possible solutions of a puzzle through brute-force trial and error. DLX is the method of choice for rapid finding of all possible solutions to Sudoku puzzles."
	s+="</td></tr></table>"

	s+="<pre style='font-size:8pt'>"
	s+="       cell constraints (only one of value in each of 81 cells)------------------------- row constraints (only one of 1-9 in each of 9 rows)------------------------------ column constraints (only one of 1-9 in each of 9 columns------------------------- block constraints (only one of 1-9 in each of 9 blocks)--------------------------\n"
	s+="       0        1         2         3         4         5         6         7         8  1        2        3        4        5        6        7        8        9         1        2        3        4        5        6        7        8        9         1        2        3        4        5        6        7        8        9        \n"
	s+="       123456789012345678901234567890123456789012345678901234567890123456789012345678901 123456789123456789123456789123456789123456789123456789123456789123456789123456789 123456789123456789123456789123456789123456789123456789123456789123456789123456789 123456789123456789123456789123456789123456789123456789123456789123456789123456789\n"
	s+="\n"
	s+="cand/N "
	S.push(s);s=""
	for(var ii=0;ii<9;ii++)for(var jj=0;jj<9;jj++)s+=xNumP(Possible.XCell[ii][jj])
	s+=" "
	for(var ii=0;ii<9;ii++)for(var jj=0;jj<9;jj++)s+=xNumP(Possible.XRow[ii][jj])
	s+=" "
	for(var ii=0;ii<9;ii++)for(var jj=0;jj<9;jj++)s+=xNumP(Possible.XCol[ii][jj])
	s+=" "
	for(var ii=0;ii<9;ii++)for(var jj=0;jj<9;jj++)s+=xNumP(Possible.XBlock[ii][jj])
	s+=" "
	s+="\n"
	S.push(s);s=""
          //r1c1#1 o                                                                                |o                                                                                |o                                                                                |o                                                                                x

	for(var i=0;i<9;i++)for(j=0;j<9;j++){
		for(k=0;k<9;k++){
			coord=coordOf(Data[i][j],k)
			if(Data[i][j].N==k+1)coord="<font color=red>"+coord+"</font>"
			s+=coord+" "
			for(var ii=0;ii<9;ii++)for(var jj=0;jj<9;jj++)s+=(i==ii && j==jj &&(Possible.XCell[i][j]&Pwr2[k])?(Data[i][j].N==k+1?"<font color=red>"+(k+1)+"</font>":k+1):" ")
			s+="|"
			for(var ii=0;ii<9;ii++)for(var jj=0;jj<9;jj++)s+=(i==ii && k==jj &&(Possible.XCell[i][j]&Pwr2[k])?(Data[i][j].N==k+1?"<font color=red>"+(k+1)+"</font>":k+1):" ")
			s+="|"
			for(var ii=0;ii<9;ii++)for(var jj=0;jj<9;jj++)s+=(j==ii && k==jj &&(Possible.XCell[i][j]&Pwr2[k])?(Data[i][j].N==k+1?"<font color=red>"+(k+1)+"</font>":k+1):" ")
			s+="|"
			for(var ii=0;ii<9;ii++)for(var jj=0;jj<9;jj++)s+=(Data[i][j].block==ii && k==jj &&(Possible.XCell[i][j]&Pwr2[k])?(Data[i][j].N==k+1?"<font color=red>"+(k+1)+"</font>":k+1):" ")
			s+="|"
			s+="\n"
			S.push(s);s=""
		}
		s+="----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------\n"
	}
	s+="</pre>"
	S.push(s)
	return S.join("")
}

function getRuud(){
	var s=""
	var i=0
	var k=0
	s+="<table><tr><td valign=top><h3>Unresolved Possibilities Table</h3>"
	s+="This table is really four tables in one.<br><br>On the left we have <b>Rows (by col)</b>. This table is a depiction of the current state of the puzzle row by row, column by column."
	s+=" The cells are listed by row and column number. This is basically what you see if you use 3D View and select a specific row. "
	s+=" For each of the 81 cells, all remaining candidates are listed vertically." 
	s+=" (If no number is shown for a given cell, it means that number has been assigned as is no longer possible anywhere else.)"
	s+=" On this table you may see naked and hidden singles, pairs, triples, and the like."
	s+="<br><br>The next two tables, <b>Columns (by row)</b> and <b>Blocks (by cell)</b>, list the same information for specific columns and blocks."
	s+="<br><br>The last table, <b>Digits (by col)</b>, is organized by candidate value. It is what people generally use for coloring analysis. In it you may see singles, X-Wings, Swordfish, and the like."

	s+="<br><br>When all tables are blank, no more constraints remain to be fulfilled, and the puzzle is solved."

	s+="</td>"
	s+="<td><table cellspacing=1 cellpadding=1 border=1><tr>"
	s+="<tr><th></th><th>Rows<br>(by col)&nbsp;<pre>123456789</pre></th><th></th><th>Columns<br>(by row)&nbsp;<pre>123456789</pre></th><th></th><th>Blocks<br>(by cell)<pre>123456789</pre></th><th></th><th>Digits<br>(by col)<pre>123456789</pre></th></tr>"

	for(var ii=0;ii<9;ii++){
		s+="<tr>"
		i=ii
		s+="<th>Row "+(i+1)+"</th>"
		//cells: ii is i here; my XCell[i][j] is a ROW on this table
		s+="<td><pre>"
		for(var k=0;k<9;k++){
			for(var j=0;j<9;j++)s+=((Possible.XCell[i][j]&Pwr2[k])&&!Data[i][j].N?k+1:" ")
			s+="\n"
		}
		s+="</pre></td>"


		j=ii
		s+="<th>Col "+(j+1)+"</th>"
		//cells: ii is j here; my XCell[i][j] is a COLUMN on this table
		s+="<td><pre>"
		for(var k=0;k<9;k++){
			for(var i=0;i<9;i++)s+=((Possible.XCell[i][j]&Pwr2[k])&&!Data[i][j].N?k+1:" ")
			s+="\n"
		}
		s+="</pre></td>"

		i=ii
		s+="<th>Block "+(i+1)+"</th>"
		//blocks: ii is i here; my XBlockCell[i][p] is a ROW on this table
		s+="<td><pre>"
		for(var k=0;k<9;k++){
			for(var j=0;j<9;j++)s+=((Possible.XBlockCell[i][j]&Pwr2[k])&&!Blocks[i][j].N?k+1:" ")
			s+="\n"
		}

		k=ii
		s+="<th>Digit "+(k+1)+"</th>"

		//cols: ii is k here; my XCol[j][k] is a COLUMN on this table
		s+="<td><pre>"
		for(var i=0;i<9;i++){
			for(var j=0;j<9;j++)s+=(Possible.XCol[j][k]&Pwr2[i]?k+1:" ")
			s+="\n"
		}
		s+="</pre></td>"

/* Ruud's
		//rows: ii is k here; my XRow[i][k] is a COLUMN on this table
		s+="<td><pre>"
		for(var j=0;j<9;j++){
			for(var i=0;i<9;i++)s+=(Possible.XRow[i][k]&Pwr2[j]?k+1:" ")
			s+="\n"
		}
		s+="</pre></td>"

		s+="<td><pre>"
		for(var i=0;i<9;i++){
			for(var j=0;j<9;j++)s+=(Possible.XBlock[j][k]&Pwr2[i]?k+1:" ")
			s+="\n"
		}
		s+="</pre></td>"
*/

		s+="</tr>"
	}
	s+="</table>"
	s+="</td></tr></table>"
	s+=" based on an idea of <a target=_blank href=http://home.quicknet.nl/qn/prive/r.vanderwerf/>Ruud van der Werf</a>"
	return s	
}

/*

[Puzzle]
19.342..5
..5....3.
......2.9
.....5..4
...1.4...
7..6.....
9.8......
.2....1..
3..921.68
[State]   (optional)
19.342..5
2.5.1943.
4.3...219
..12.5..4
..91.4.2.
7426...51
918....42
.2.4..19.
3.4921.68
[PencilMarks]
,,67,,,,678,78,
68,678,,78,68,678,67,,67
68,78,,578,56,67,,7,
68,36,,78,3789,,36789,78,
568,356,,,378,,3678,78,367
,38,,,389,38,389,8,3
,567,,57,356,367,357,7,37
56,,67,578,358,378,,7,37
,57,7,,,,57,,

*/

function readSDKdata(sdata){
	sdata=sdata.substring(sdata.indexOf("[Puzzle]")).split("[Colo")[0]
	var s=sdata.replace(/\[|\]/g,"|")
	var S=s.split("|")
	var puzzle=S[2].replace(/\n|\r|\ /g,"")
	var state=(S.length < 6 ? puzzle : S[4].replace(/\n|\r|\ /g,""))
	var Marks=S[S.length - 1].replace(/\n|\r|\ /g,",").split(",")
	var sout="["
	for (var i=0;i<81;i++){
		sout+=","
		if(puzzle.charAt(i)!="."){
			sout+="[-"+puzzle.charAt(i)+"]"
		}else if(state.charAt(i)!="."){
			sout+="["+state.charAt(i)+"]"
		}else if(Marks[i+1]!=""){
			sout+="["
			for(var j=0;j<Marks[i+1].length;j++)sout+=","+Marks[i+1].charAt(j)
			sout+="]"
		}
	}
	sout+="]"
	sout=sout.replace(/\[\,/g,"[")
	return sout
}

function logGetMessage() {
 return Msgs.join("<br />")
}

function logAddMessage(s,sinfo, ishow){
	if(!iShowAnswers && !ishowdetail && !ishow)return
	if(!idolog)return
	if(typeof(s)=="object")s="<br />"+s.join("<br>").replace(/\n/g,"<br>")
	if(sinfo)s="<b>"+sinfo+": </b>"+s
	Msgs[Msgs.length] = s
}

function logMessage(s,iclear,isappend,fromWhere){
//alert("log "+iclear + " " + isappend + " from:" + fromWhere + "\n"+s)
	if(!idolog)return
	var d=document.getElementById("logmsg")
	s = s.replace(/name\=/g,"href=")
	if(iclear){
		d.innerHTML=s
	}else if(isappend){
		d.innerHTML+=s
	}else{
		d.innerHTML=s+d.innerHTML
	}
	Msgs=[]
}

function showChainDiv(n, n2, ext,list) {
	if (!ext)ext=""
	if (!n2)n2=0
	var d=document.getElementById("chains")
	if (n < 0){
		d.innerHTML=""
		return
	}
	if (n2 > 0 || list) {
		var s = ""
		for(var i=1;i<StrongChains.length;i++)
			if(i == n || i == n2 || list.indexOf(";"+i+"(")>=0) 
				s += showChainDiv(i,-1,(i == n ? "" : "a"))
		d.innerHTML = s
		return
	}
	if (n2>=0)nY = 0
	var List = []
	for(var e=0;e<StrongEdges.length;e++){
		var Ainfo = StrongEdges[e][0]
		var Binfo = StrongEdges[e][1]
		if(Ainfo.length>2||Binfo.length>2){
			//must be strong group
			continue
		}
		var iA=Ainfo[1][0].row
		var jA=Ainfo[1][0].col
		var kA=Ainfo[0]
		var bA=Ainfo[1][0].block
		var ichain = chainOf(iA,jA,kA)
		if(ichain!=n)continue

		var iB=Binfo[1][0].row
		var jB=Binfo[1][0].col
		var kB=Binfo[0]
		var bB=Binfo[1][0].block
		if (iA==iB && jA==jB){
			List.push(getClink(iA, jA, kA, kB,ext))
			//same cell
		} else if (iA==iB) {
			//same row
			if(kA==kB)
				List[List.length]=getHlink(iA, Math.min(jA,jB),kA,Math.abs(jB-jA),ext)
		} else if (jA==jB) {
			//same col
			if(kA==kB)
				List[List.length]=getVlink(Math.min(iA,iB),jA,kA,Math.abs(iB-iA),ext)
		} else if (bA==bB){
			//same block
			if(kA==kB)
				List[List.length]=getBlink(Math.min(iA,iB),Math.min(jA,jB),kA,iB-iA,jB-jA,ext)
		}
	}
	var s = List.join("")
	if(n2<0)return s
	d.innerHTML = s + (n2 > 0 ? showChainDiv(n2,-1,"a") : "")
}


var nY = 0

function testit(i,j,k) {
  nY = 0;
  var s = "<table width='500'><tr><td>"
  s += getHlink(i, j, k, 8,"");
  s += getHlink(i+1, j, k, 7,"")
  s += getHlink(i+2, j, k, 6,"")
  s += getHlink(i+3, j, k, 5,"")
  s += getHlink(i+4, j, k, 4,"")
  s += getHlink(i+5, j, k, 3,"")
  s += getHlink(i+6, j, k, 2,"")
  s += getHlink(i+7, j, k, 1,"")
  s += getHlink(i+8, j+2, k, 2,"")
  s += getVlink(i, j, k, 8,"")
  s += getVlink(i, j+1, k, 7,"")
  s += getVlink(i, j+2, k, 6,"")
  s += getVlink(i, j+3, k, 5,"")
  s += getVlink(i, j+4, k, 4,"")
  s += getVlink(i, j+5, k, 3,"")
  s += getVlink(i, j+6, k, 2,"")
  s += getVlink(i, j+7, k, 1,"")
  s += getVlink(i+2, j+8, k, 2,"")
  s += "</td></tr></table>"
  var d = document.getElementById("chains")
  d.innerHTML=s
}

Vheights=[80,134,185,238,295,350,404,461]

function getVlink(i,j,k,n,ext){
  return getLink(i,j,k,53,Vheights[n-1],n,0,-2+k%3,-491+2,ext)
}

function getHlink(i,j,k,n,ext){
  return getLink(i,j,k,Vheights[n-1],53,0,n,4,-510,ext)
}

function getBlink(i,j,k,n1,n2,ext){
  if(n2 < 0){
	n2 = -n2
	n1 = -n1
  }
  var w=(n2 == 1 ? 80 : 134)
  var h=(n1 == 1 || n1 == -1 ? 80 : 134)
  return getLink(i,j,k,w,h,n1,n2,4,-492,ext)
}

function getClink(i,j,k1,k2,ext){
  var kj1 = k1%3
  var kj2 = k2%3
  var ki1 = (k1-kj1)/3
  var ki2 = (k2-kj2)/3
  var n1 = ki2-ki1
  var n2 = kj2-kj1
  if(n2 < 0){
	n2 = -n2
	n1 = -n1
  }
  if(n2 == 0)n1=Math.abs(n1)
  var k = Math.min(ki1,ki2)*3 + Math.min(kj1, kj2)
  return getLink(i,j,k,47,50,"0"+n1,n2,7,-487,ext)
}

function getLink(i,j,k,w,h,n1,n2,x0,y0,ext){
  var x = (j) * 53 + (j-j%3)/3 + k%3 * 13 + x0 +2
  var y = (i) * 53 + (i-i%3)/3 + (k-k%3)/3 * 13 +y0 - getYAdjust() - nY + 2
  nY += h
  return "<div  width='"+w+"' height='"+h+"' style='position:relative;left:"+x+";top:"+y+"'><img width='"+w+"' height='"+h+"' src='"+n1+""+n2+ext+".gif' /></div>"
}


function encodeMarks(T){
	var s="$"
	for(var i=0;i<9;i++)for(var j=0;j<9;j++){
		if(Data[i][j].fixed){
			s+=-Data[i][j].N
			continue
		}
		var n = 0
		var p1 = 0
		var p2 = 0
		var k1 = -1
		var s1 = ""
		var s2 = ""
		for(var k=4;k<9;k++)if(T[k+j*9+i*81]){
			n++
			p2 |= Pwr2[k-4]
			k1 = k
		}
		var s2 = (n > 0 ? encodingList.charAt(p2 + 32) : "")
		for(var k=0;k<4;k++)if(T[k+j*9+i*81]){
			n++
			p1 = p1 | Pwr2[k]
			k1 = k
		}
		var ch
		switch(n){
		case 0:	ch="@";break
		case 1: ch=(k1+1);break
		case 9: ch="P";break
		default: 
			ch=(p1 > 0 ? encodingList.charAt(p1 + (s2 ? 16 : 0)): "") + s2
		}
		s += ch
	}
	return s
}

var encodingList="@ABCDEFGHIJKLMNOPQRSTUVWXYZ[+],_`abcdefghijklmnopqrstuvwxyz{(})."
		// P == all possible;
		// @ == none possible  
                // 41...........4F                   // 1-4, none higher
                //                51...........5F    // 1-4, also higher
						// 5-9
				                // 61...........................7F;
function decodeMarks(s) {
	var sout = ""
	for (var i = 1; i < s.length; i++){
		var ch = s.charAt(i)
		sout +=","
		if (ch == "-") {
			sout += ch + s.charAt(++i)
			continue
		}
		var pt1 = encodingList.indexOf(ch)
		if (pt1 == 16) {
			pt1 = 0x1FF
		} else if (pt1 > 32) {
			pt1 = pt1 << 4
		} else if (pt1 > 16) {
			pt1 += (encodingList.indexOf(s.charAt(++i)) << 4) - 16
		}
		if (pt1 < 0) {
			sout += ch
			continue
		}
		for(var k=0;k<9;k++)if(pt1 & Pwr2[k])sout+=(k+1)
	}
	return sout.substring(1)
}


function getGlyphTable() {
	var s = "<table border=0><tr>"
	if(idoglyphs) {
		for (var n = 1; n <= 9; n++)s +="<td width=50 align=center><span onmouseup=doShow("+n+") title='show candidate "+n+"'>" 
		+ getGlyphTableEntry(n-1)+"</span></td>"
		s += "</tr><tr>"
	}
	for (var n = 1; n <= 9; n++)s +="<th style='font-size:8pt'><a id=s"+n+" href=javascript:doShow(" +n+")>"+n+"</a></th>"
	s += (idoglyphs ? "</tr><tr><td colspan=9 align=center>" : "<td align=center>&nbsp;&nbsp;&nbsp;&nbsp;") 
	+ '<span style="font-size:10pt"><span title="hover over a number to set its color temporarily"> tag color</span>: '
	+' <link><input type=radio name=radtag id=radtag0 onclick=clearMouseColors()>none</link>'
	+' <link><input type=radio name=radtag id=radtag1 checked>yellow</link>'
	+' <link><input type=radio name=radtag id=radtag2>purple</link>'
	+" <a href=javascript:setMouseColor(1) title='set all tagged candidates of this color TRUE'>set</a>"
	+" <a href=javascript:setMouseColor(0) title='for cells with only two candidates, set the OTHER c>not</a></span></td>"

	s += "</tr></table>"
	return s
}

function getGlyphTableEntry(k) {
	var S = ["<table border=1 cellspacing=0 cellpadding=0><tr><td><table cellspacing=0 cellpadding=0 border=0>"]
	var id = k*1000
	for (var i = 0; i < 3; i++) {
		S.push("<tr>")
		for (var j = 0; j < 3; j++) {
			S.push("<td><table cellspacing=0 cellpadding=0>")
			for (var r = 0; r < 6; r++) {
				S.push("<tr height=2>")
				for (var c = 0; c < 6; c++) {
					id++ 
					S.push("<td id=gt"+id+" width=2 style='background-color:white'></td>")
					//S.push("<td id=gt"+id+" width=2><img id=gi"+id+" src=white.gif width=2 height=2></td>")
				}
				S.push("</tr>")
			}
			S.push("</table></td>")
		}
		S.push("</tr>")
	}
	S.push("</table></td></tr></table>")
	return S.join("")
}


function setGlyphs(doshow) {
	if(!idoglyphs)return
	resetGlyphs()
	for (var k = 0; k < 9; k++)setGlyph(k, doshow)
}

function setGlyph(k,doshow) {
	var g = Glyphs[k]
	var id = k*1000
	var bp
	for (var i = 0; i < 3; i++) {
		for (var j = 0; j < 3; j++) {
			var b = i * 3 + j
			var gr = i * 6
			var col = (b%2 ? "#A0A0A0" : "white")
			for (var r = 0; r < 6; r++) {
				var gc = j * 6
				for (var c = 0; c < 6; c++) {
					var iscell = (r%2 == 0 && c%2 == 0)
					var isone = (iscell && Blocks[b][bp=(r/2)*3 + c/2].onlypossible == k + 1)
					var isk = (iscell && !isone && (Possible.XBlock[b][k] & Pwr2[bp]))
					var x = (!doshow || (g[gr] & Pwr2[gc++]) == 0 ? col : isone ? "red" : isk ? "green" : "black")
					id++
					document.getElementById("gt" + id).style.backgroundColor = x
					//document.getElementById("gi" + id).src = x + ".gif"
				}
				gr++
			}
		}
	}
}

