//test1: MARKS=[[1,4],2,3,7,5,9,[1,4,8],[1,4,8],6,8,7,[1,4],2,6,[1,3,4],5,9,[1,3,4],9,[5,6],[5,6],[1,3,8],[1,3,8],[1,3,4,8],7,2,[1,3,4],[2,6],[1,5,6],[1,2,5,6,8],[1,3,5,8],4,[1,3,8],9,7,[1,5,8],3,[1,4,5],7,[1,5,8],9,6,[1,4,8],[1,4,5,8],2,[1,4],9,[1,4,5,8],[1,5,8],2,7,[3,6],[3,6],[1,4,5,8],5,[1,3,6],[1,2,6,9],4,7,[1,3,8],[1,2,3,6,8],[1,3,6,8],[1,8,9],[6,7],[1,3,4,6],[1,4,6,9],[6,9],[1,3,8],2,[1,3,4,6,8],[1,3,4,5,6,8],[1,4,5,7,8,9],[2,6,7],8,[1,2,4,6,9],[6,9],[1,3],5,[1,2,3,4,6],[1,3,4,6],[1,4,7,9]]

//test2: SHHALS&WEAKONLY&NOGLYPHSMARKS=$-6X(-2Y(-5XtUxQtTpQ(}Q}Q(Qr-3Qz-4-2U(X(]xY(Yr-2QzQws-4-3wQu-2-8QrQss(-1.+u+pXw-2TsXhRxR{{]q]pXs-7UsXh-5Xz]z-2-7Yp+rrTrRtZvXv+q+pXqXs-8-1UpXp]p-6-8Yq+q-2-7

//test3: SHHALS&&NOGLYPHSMARKS=$-6X(-2U(-5TtYxQtXpQ(}Q}Q(Qr-4Qz-3-2U(X(]xQ(Qr-2YzQwXs-4-3wQu-2-8QrQss(-1.Xu+pTw-2XsThRxR{{]q]pTs-7YsTh-5Xz]z-2-7Up+rrXrVtRvTvXq+pTqTs-8-1UpXp]p-6-8Uq+q-2-7

//test4: (hangs) SHHALS&&NOGLYPHSMARKS=1,4,3,5,8,67,9,267,26,8,25,25,4,9,67,17,3,16,9,7,6,3,2,1,45,48,458,27,1,4,6,57,258,3,289,289,6,3,258,28,14,9,45,1248,7,257,2589,2589,278,14,3,6,1248,12458,23,289,1,2789,36,4,78,5,69,4,6,2589,279,57,25,178,17,3,35,589,7,1,36,58,2,469,469

// added almost-locked sets in Y and X 5:41 PM 12/11/2005
// adjusted A1 vs. A2 12:51 PM 12/17/2005
// added forceAlmostLockedTrue() for X-type  12:50 PM 12/17/2005 (no major effect)
// added sashimi 9:26 AM 1/2/2009


AlmostLocked={} //[sid,xtype,isgrid,SingleK,xCellCheck,[[C.showinfo,(isgrid?z:k),C],...(weakly associated nodes)]]
ALSWeakLinks={}
ALSWeakNodes={}
ALSCatalog={} //[sindex,celllist,klist]
AlsChains = [0]

//indices into ALmostLocked

ALSINDEX=0
ALSID=1
ALSTYPE=2
ALSGRIDTYPE=3
ALSCELLCOUNT=4
ALSDATASINGLEK=5
ALSDATACELLCHECK=6
ALSNODES=7
ALSWEAKNODES=8

function getAlmostLocked(idonochecks){
	nalmostlockedb=0
	nalmostlockedx=0
	nalmostlockedy=0
	AlmostLocked={}
	ALSWeakLinks={}
	AlsChains=[0]
	ALScatalog={}
	ALSWeakNodes={}
	ALSCatalog.Row=[]
	ALSCatalog.Col=[]
	ALSCatalog.Block=[]
	ALSCatalog.GridRow=[]
	ALSCatalog.GridCol=[]
	globalWeakList = ""

	if(!idonochecks)idonochecks=0

	var p
	var q
	for(var i=0;i<9;i++){
		ALSCatalog.Row[i]=[[],[],[],[],[],[],[],[],[]]
		ALSCatalog.Col[i]=[[],[],[],[],[],[],[],[],[]]
		ALSCatalog.Block[i]=[[],[],[],[],[],[],[],[],[]]
		ALSCatalog.GridRow[i]=[[],[],[],[],[],[],[],[],[]]
		ALSCatalog.GridCol[i]=[[],[],[],[],[],[],[],[],[]]
	}

	if (!igetalmostlockedb && !igetalmostlockedy)return
	var C
	for(var i=0;i<9;i++)for(var j=0;j<9;j++) {
		if (isalsfull && igetalmostlockedy && (C = Data[i][j]).npossible==2)
			addAlmostLocked(1,2,C.block,0,Possible.XCell[i][j],Pwr2[C.bptr],"BlockCell",0)
		if (!igetalmostlockedb || xNum((q=Possible.XBlock[i][j])) <= 2)continue
		// looking for almost-locked blocks
		// because these transmute weak links
		// -- one row, one column
		var done = false

		for (var k = 0; k < 9; k++) {
			if((q|BlockRCTypes[k])==BlockRCTypes[k] && xNum(Possible.XBlockCol[i][j])>=2 && xNum(Possible.XBlockRow[i][j])>=2) {
				addAlmostLocked(1,2,j,0,i,k,"Block-X2",0,0, idonochecks)
				done = true;
				break;
			}
		}
		// -- four or more in only two rows
		if (!done && xNum(p=Possible.XBlockRow[i][j])==2)
			addAlmostLocked(1,2,j,0,i,-p,"Block-R2",0,0, idonochecks)
		// -- four or more in only two columns
		if (!done && xNum(p=Possible.XBlockCol[i][j])==2)
			addAlmostLocked(1,2,j,0,i,-p,"Block-C2",0,0, idonochecks)
	}

	// Patrick Bastide's idea:
	// in any row, two of three subsets form an almost-locked set:
	if (igetalmostlockedb && isalsv2) {
		for(var i=0;i<9;i++)for(var k=0;k<9;k++) {
			if(xNum(Possible.XRow[i][k]) > 2 && xNum(Possible.XRowSubset[i][k]) == 2) {
				addAlmostLockedRange(i,k,"Row",Possible.XRow[i][k], idonochecks)
			}
			if(xNum(Possible.XCol[i][k]) > 2 && xNum(Possible.XColSubset[i][k]) == 2) {
					addAlmostLockedRange(i,k,"Col",Possible.XCol[i][k], idonochecks)
			}
		}
	}
}

var BlockRCTypes=[ 
	// bitmap of the nine ways of having one row, one column
	// the index into this array is the crossing point
	// 0 1 2   x x x
	// 3 4 5   x o o  == [0] = 001 001 111 (read from right to left) 
	// 6 7 8   x o o         = 0x04F
	 0x04F	// 0 0100 1111
	,0x097	// 0 1001 0111
	,0x127	// 1 0010 0111
	,0x079	// 0 0111 1001
	,0x0BA	// 0 1011 1010
	,0x13C	// 1 0011 1100
	,0x1C9	// 1 1100 1001
	,0x1D2	// 1 1101 0010
	,0x1E4	// 1 1110 0100
]

function addAlmostLockedRange(i, k, type, celllist,idonochecks) {
	var xData=xCreateAlsData()
	var isRow = (type == "Row")
	var sindex = " { "
	var lastset = -1
	test = ""
	var Sets = [[],[]]
	var nSets = [0,0]
	var iset = -1
	var snodes = ""
	
	for (var j = 0; j < 9; j++) if (celllist&Pwr2[j]) {
		var C = (isRow ? Data[i][j] : Data[j][i])
		snodes+=coordOf(C,k)
		var thisset = Math.floor(j / 3)
		if (thisset != lastset) {
			if (sindex.length > 3)
				sindex += "}{ "
			iset++
			lastset = thisset
		}
		nSets[iset]++
		Sets[iset].push(C)
		sindex += coordOf(C,k) + " "
		xSetAlsData(xData,C,k,true,true)
	}
	sindex+="}"

	var sindex = (nalmostlockedb + nalmostlockedx + nalmostlockedy + 1) + " 1x2ALS-"
		+ "R#"+(k+1)+"-"+type+"("+sindex.substring(1)+")"
	if(AlmostLocked[sindex])return 0
	nalmostlockedb++
	sid="R"+type.charAt(0)+nalmostlockedb
	A=AlmostLocked[sindex]=[AlsChains.length,sid,type,3,xNum(celllist),xData.SingleK,xData.xCellCheck,snodes,[]]
	AlsChains.push(sindex)

	if (idonochecks)return 
	 // two sets here: 
	 // for each, set nodes to be all others in this block
	 // if n = 1 for this row/column set, also set all in this column/row to be nodes
	var Ret = [""]
	var klist = Pwr2[k]
	for(var i = 0; i < 2; i++) {
		var n = nSets[i];
		var Set = Sets[i];
		addToCatalogBlock(Set[0].block, k, xData, A, snodes, sindex, i + 1, klist, k, Ret)
		if (n == 1) {
			addToCatalogCell(Set[0].row, Set[0].col, k, A, snodes, sindex, i + 1, k, Ret)
			if (isRow)
				addToCatalogCol(Set[0].col, k, xData, A, snodes, sindex, i + 1, klist, k, Ret)
			else
				addToCatalogRow(Set[0].row, k, xData, A, snodes, sindex, i + 1, klist, k, Ret)
		}
	}
	 	
	if(ishowdetail)logAddMessage(sindex + " Range-" + type + " celllist="+xStr(celllist)+" klist="+xStr(klist)
		+"<br> weak nodes: "+Ret[0] +"<br> "+ dumpAlsData(xData))

}

function addToCatalogRow(row, kreal, xData, A, snodes, sindex, kpt, klist, knode, Ret, isgrid, maxdim, celllist, P) {
	ALSCatalog.Row[row][kreal].push([sindex,xData.xCk[kpt],klist,kpt])
	var T=xList(Possible.XRow[row][kreal])
	var isOK = 1
	for (var j = 0; j < T.length;j++) {
		isOK&=addALSWeakNode("Row",A,snodes,sindex,isgrid,kreal,kpt,knode,Data[row][T[j]],Ret,P,celllist,klist,T[j],maxdim)
		if(!solving&&!isOK)break
	}
	return isOK
}

function addToCatalogCol(col, kreal, xData, A, snodes, sindex, kpt, klist, knode, Ret, isgrid, maxdim, celllist, P) {
	ALSCatalog.Col[col][kreal].push([sindex,xData.xRk[kpt],klist,kpt])
	var T=xList(Possible.XCol[col][kreal])
	var isOK = 1
	for (var j = 0; j < T.length;j++) {
		isOK&=addALSWeakNode("Col",A,snodes,sindex,isgrid,kreal,kpt,knode, Data[T[j]][col],Ret,P,celllist,klist,T[j],maxdim)
		if(!solving&&!isOK)break;
	}
	return isOK
}

function addToCatalogBlock(b, kreal, xData, A, snodes, sindex, kpt, klist, knode, Ret, isgrid, maxdim, celllist) {
	ALSCatalog.Block[b][kreal].push([sindex,xData.xPk[kpt],klist,kpt])
	var T=xList(Possible.XBlock[b][kreal])
	var isOK = 1
	for (var j = 0; j < T.length;j++) {
		isOK&=addALSWeakNode("Block",A,snodes,sindex,isgrid,kreal,kpt, knode, Blocks[b][T[j]],Ret,0,celllist,klist,T[j],maxdim)
		if(!solving&&!isOK)break
	}
	return isOK
}

function addToCatalogCell(row, col, kreal, A, snodes, sindex, kpt, knode,Ret) {
	var C=Data[row][col]
	var T=xList(Possible.XCell[row][col]^Pwr2[kreal])
	var isOK = 1
	for (var j = 0; j < T.length;j++) {
		isOK&=addALSWeakNode("Cell",A,snodes,sindex,false, knode,T[j],kpt,C,Ret)
		if(!solving&&!isOK)break
	}
	return isOK
}

function addAlmostLockedWeakLink(sindex1,sindex2,k, kpt1, kpt2){

	// the two almost locked sets are weakly linked by kpt1 and kpt2
	var s=sindex1+"_"+sindex2
	//[sindex1,sindex2,xK,nK]
	var A=ALSWeakLinks[s]
	if(!A)A=ALSWeakLinks[s]=[sindex1,sindex2,0,0]
	var x = Pwr2[k]
	if (A[2]&x)return
	A[2]|= x
	A[3]++
	if(ishowdetail)logAddMessage("adding weak link for #" + (k + 1) + ": " + s)
	if(!isalsv2)return

	// now add weak links of A-!kpt1 to B-kpt2 and vice-versa
	// because when A-!kpt1 is forced F, then A-kpt1 is forced T, forcing B-kpt2 false
	var A = AlmostLocked[sindex1]
	var B = AlmostLocked[sindex2]
	var AW = A[ALSWEAKNODES]
	var BW = B[ALSWEAKNODES]

	var snodes = B[ALSNODES]
	var Ret = ["" + BW + "|"]
	for (var i = 0; i < AW.length; i++)if (AW[i][3] != kpt1) {
		var C = AW[i][2]
		var k = AW[i][1]
		var knode = AW[i][5]
		addALSWeakNode("-- via almost-locked sets (ALS" + sindex1 + " is linked to ALS" + sindex2 + " by value " + (knode + 1) + ", so all weak links of one set not involving this value become weak links of the other)",B,snodes,sindex2,true,k,kpt2,knode,C,Ret)
	}
	Ret=Ret[0].split("|")[1]
	if(ishowdetail && Ret)logAddMessage("adding weak links for " + sindex2 + ": " + Ret)
	var snodes = A[ALSNODES]
	var Ret = ["" + AW+ "|"]
	for (var i = 0; i < BW.length; i++)if (BW[i][3] != kpt2) {
		var C = BW[i][2]
		var k = BW[i][1]
		var knode = BW[i][5]
		addALSWeakNode("-- via almost-locked sets (ALS" + sindex2 + " is linked to ALS" + sindex1 + " by value " + (knode + 1) + ", so all weak links of one set not involving this value become weak links of the other)",A,snodes,sindex1,true,k,kpt1,knode,C,Ret)
	}
	Ret=Ret[0].split("|")[1]
	if(ishowdetail && Ret)logAddMessage("adding weak links for " + sindex1 + ": " + Ret)
}


function addALSWeakNode(type,A,snodes,sindex,isgrid,kreal,kpt,knode,C,Ret,P,celllist,klist,rc,maxdim) {
	var slist = Ret[0]
	var s=coordOf(C,kreal)
	if(snodes.indexOf(s)>=0) return 1
	var skey=s+"," + kpt
	var p
	if(slist.indexOf(skey)>=0) return 1
	if (isgrid && (!idosashimi || idoalslarge || (p=xNum(celllist)) < maxdim)) {
		if (P && idosashimi) {
		/* sashimi check: node TRUE must not reduce any other row/column to the reduced and locked set

			--------kpt-------->
		   |     X     X        X    ALS
                   r     X     X             ALS
		   c	--------------------
		   |  r             |   C    ELIMINATED
		   |  c  X     X    | X   X  EXCLUDED
		   V  s  X     X    |   X    EXCLUDED
		      e              -kset--
		      t --------------------

			check rcset for marks not in kset
			if these then are a subset of klist, C can be eliminated		

		*/

			var kset = 7<<(Math.floor(kpt/3)*3)
			var rcset = (7<<(Math.floor(rc/3)*3)|celllist)^celllist^Pwr2[rc]
			for (var i = 0; i < 9; i++) if ((rcset & Pwr2[i]) && ((p = P[i][kreal])|klist)!=klist){
				var x = (p|kset)^kset
				if (x != 0 && (x|klist)==klist){	// this is the problem.
					eliminateK(C,kreal,s=s + " Sashimi in relation to almost-locked set " + sindex)
					greenlist+=sindex + "ALSPOINT3"
					showAlsChain(sindex)
					x = P[i][kreal]
					for (var j = 0; j < 9; j++)if (x&Pwr2[j])bluelist+=coordOf(type == "Col"?Data[i][j]:Data[j][i],kreal)
					addAutoMsg("A1s",s)
					return 0
				}
			}			


		}
		if (slist.indexOf(s + ",") >=0) {
			//node to two components!
			eliminateK(C,kreal,s =s + " is doubly weakly linked to almost-locked set " + sindex)
			greenlist+=sindex + "ALSPOINT6"
			showAlsChain(sindex)
			addAutoMsg("A1g",s)
			return 0
		}
	}
	if(!ALSWeakNodes[s])ALSWeakNodes[s]=[]
	A[ALSWEAKNODES].push([C.showinfo,kreal,C,kpt,type,knode])
	ALSWeakNodes[s].push([sindex,kreal,kpt])
	Ret[0] = slist + " " + skey
	return 1
}


test = ""
function xSetAlsData(A,C,k,xaddrow,xaddcol){
	A.Nk[k]++
	A.SingleK[k]=(A.Nk[k]==1 ? C : 0)
	if(xaddrow)A.xRk[k]|=Pwr2[C.row]
	if(xaddcol)A.xCk[k]|=Pwr2[C.col]
	A.xBk[k]|=Pwr2[C.block]
	test += " " + C.block
	A.xPk[k]|=Pwr2[C.bptr]
	A.xCellCheck[C.row] |= Pwr2[C.col]
}




////////////// --- A1 ---  at subset time


function addAlmostLocked(nx,ny,z,isgrid,klist,celllist,type,iscanfirstindex,maxdim, idonochecks){
//                                      jlist, ilist    Row/Col
//                                      iblock, rctype   Block-

	if (ny==1 && !isgrid)return 0
	if (!idoalscolumns && type=="Col")return 0
	//grid Row/Col and iscanfirstindex
	//row/col Cell and 0/1 for iscanfirstindex
	//block BlockCell
	var xtype
	var rctype
	var iblock
	var rcmask
	var NodeMasks
	var A = []
	var isblock = (type.indexOf("Block-")==0)
	var isblockdisjoint=true
	var xaddrow=true
	var xaddcol=true

	if (isblock){
		NodeMasks=[]
		xtype = type
		type = (type=="Block-C2"?"Col" : "Row")
		if (type != "Col")xaddcol=false
		iblock = klist
		rctype = celllist

		if (rctype < 0){
			celllist = -celllist
		} else {
			celllist = Pwr2[Blocks[iblock][rctype].row]
			isblockdisjoint=((Possible.XBlock[iblock][z]&Pwr2[rctype])==0)
		}
		// a mask for finding weak links
		rcmask = 7<<(3*(type == "Row" ? iblock%3 : Math.floor(iblock/3)))
		if (rctype >= 0)rcmask^=Pwr2[Blocks[iblock][rctype][type == "Row" ? "col" : "row"]]


	} else {
		xtype=(isgrid?type:type=="BlockCell"?"Block":iscanfirstindex?"Col":"Row")
		A=(xtype=="Block"?Blocks:Data)
	}
	var isrow=(type=="Row")
	var P=(isgrid || isblock ? Possible["X"+type] : Possible.XCell)
	var isOK = 1
	var C={}
	var T=[]
	var sindex=""
	var snodes=""
	var s=""
	var slist=""
	var kreal=0
	var sid=""
	var xData=xCreateAlsData()
	var p=0
	var pt=0
	for(var i=0;i<9;i++)if(celllist & Pwr2[i]){
		var ipt = i
		if(isblock){
			pt++
			if (rctype >= 0) {
				if (p == 0) {
					ipt = Blocks[iblock][rctype].row
					i--
					if(!isblockdisjoint) // need to consider the crossing point part of the system
						xSetAlsData(xData,Blocks[iblock][rctype],z,false,false)
				} else {
					P = Possible.XCol
					rcmask = (7<<(3*Math.floor(iblock/3)))^Pwr2[ipt]
					ipt = Blocks[iblock][rctype].col
					isrow = false
					xaddrow=false
					xaddcol=true
					type="Col"
				}
			}
			p = P[ipt][z]&rcmask
			NodeMasks.push([type,ipt,p,P[ipt][z]^p,pt])
		} else if(isgrid){
			p=P[i][z]
		}else{
			C=(iscanfirstindex?A[i][z]:A[z][i])
			p=P[C.row][C.col]
		}
		T=xList(p)
		if(T.length==2) {
			//don't include sets like {12 12 ...} because ANY naked duple can be added to ANY other set.
 			 if(slist.indexOf(s=""+T)>=0)return 0
			 slist+=s
		}
		if(isgrid || isblock){
			sindex+=" { "
			for(var j=0;j<T.length;j++){
				C=(isrow?Data[ipt][T[j]]:Data[T[j]][ipt])
				sindex+=C.showinfo+" "
				xSetAlsData(xData,C,(isblock?z:T[j]),xaddrow,xaddcol)
				if(isblock && isblockdisjoint && T.length==1){
					NodeMasks.push(isrow ? ["Col",C.col,Pwr2[C.row],Possible.XCol[C.col][z]^Pwr2[C.row],pt] 
						: ["Row",C.row,Pwr2[C.col],Possible.XRow[C.row][z]^Pwr2[C.col],pt])
				}
				snodes+=coordOf(C,z)
			}
			sindex+="}"
		}else{
			sindex+=" "+C.showinfo+"("+xStr(p)+")"
			for(var j = 0; j < T.length;j++){
				xSetAlsData(xData,C,T[j],1,1)
				snodes+=coordOf(C,T[j])
			}
		}
	}
	if (isblock && !isblockdisjoint) {
		sindex += " { " + Blocks[iblock][rctype].showinfo + " }"
		snodes+=coordOf(Blocks[iblock][rctype],z)
	}
	
	var sn = (nalmostlockedb + nalmostlockedx + nalmostlockedy + 1) + " " + xNum(celllist) + "x" + xNum(klist) +"ALS-"
	if(isblock)
		sindex=sn + "B#"+(z+1)+"-"+xtype+"("+sindex.substring(1)+")"
	else if(isgrid)
		sindex=sn + "X#"+(z+1)+"-"+type+"("+sindex.substring(1)+")"
	else
		sindex=sn +"Y("+sindex.substring(1)+")"
	if(AlmostLocked[sindex])return 0
	if(isblock){
		nalmostlockedb++
		sid="B"+xtype.charAt(7)+nalmostlockedb
	} else if(isgrid){
		nalmostlockedx++
		sid="X"+type.charAt(0)+nalmostlockedx
	}else{
		nalmostlockedy++
		sid="Y"+nalmostlockedy
	}
	A=AlmostLocked[sindex]=[AlsChains.length,sid,xtype,(isgrid ? 1 : isblock ? 2 : 0),xNum(celllist),xData.SingleK,xData.xCellCheck,snodes,[]]
	AlsChains.push(sindex)

	if (idonochecks) return

	// add to row/col/block catalog and add weak nodes:

	var Ret = [""]
	if (isblock){
		for (var i = 0; i < NodeMasks.length;i++){
			var N = NodeMasks[i]
			var type = N[0]
			var row = N[1]
			var p = N[2]
			var notp = N[3]
			var kpt = N[4]
			var isrow = (type=="Row")
			ALSCatalog[type][row][z].push([sindex,p,Pwr2[z],kpt])

		if (rctype >= 0)rcmask^=Pwr2[Blocks[iblock][rctype][type == "Row" ? "col" : "row"]]

			T=xList(Possible["X"+type][row][z])
			for (var j = 0; j < T.length;j++) {
				var C = (isrow ? Data[row][T[j]]:Data[T[j]][row])
				if (C.block != iblock)
					addALSWeakNode(type,A,snodes,sindex,false,z,kpt,z,C,Ret)
			}
		}

	}else{
	  for (var kpt=0;kpt<9;kpt++) if (xData.Nk[kpt]){
		if (isgrid) {
			ALSCatalog["Grid"+type][z][kpt].push([sindex,celllist,klist,kpt])
		}
		var kreal=(isgrid?z:kpt)
		var row=-1
		var col=-1
		if((p=xData.xRk[kpt]) != 0 && xNum(p)==1) {
			//all kpt in same row
			isOK &= addToCatalogRow(xFirst(p), kreal, xData, A, snodes, sindex, kpt, klist, kpt, Ret, isgrid, maxdim, celllist, P)
			if(!solving&&!isOK)return 1;
		}
		if((p=xData.xCk[kpt]) != 0 && xNum(p)==1) {
			//all kpt in same column
			isOK &= addToCatalogCol(xFirst(p), kreal, xData, A, snodes, sindex, kpt, klist, kpt, Ret, isgrid, maxdim, celllist, P)
			if(!solving&&!isOK)return 1;
		}
		if((p=xData.xBk[kpt]) != 0 && xNum(p)==1) {
			//all kpt in same block
			isOK &= addToCatalogBlock(xFirst(p), kreal, xData, A, snodes, sindex, kpt, klist, kpt, Ret, isgrid, maxdim, celllist, 0)
			if(!solving&&!isOK)return 1;
			if (row >= 0 && col >= 0 && isgrid) {
				//all kpt in same cell
				// single cell for this value -- only important for grid type
				isOK &= addToCatalogCell(row, col, kreal, A, snodes, sindex, kpt, kpt, Ret)
				if(!solving&&!isOK)return 1;
			}
		}
		if(solving&&!isOK)return 1
	  }
	}

	if(ishowdetail)logAddMessage(sindex + " " + xtype + " celllist="+xStr(celllist)+" klist="+xStr(klist)
		+"<br> nodes: "+Ret[0] +"<br> "+ dumpAlsData(xData))

	return !isOK
}



// A1 checks 

function checkAlmostLockedSetsX(){
	var isOK=1
	logAddMessage(nalmostlockedx+" almost-locked sets (X)")
	isOK&=!checkAlmostLocked("GridRow")
	if(isOK)isOK&=!checkAlmostLocked("GridCol")
	return !isOK
}

function checkAlmostLockedSetsY(A){
	var isOK=1
	logAddMessage(nalmostlockedy+" almost-locked sets (Y)")
	isOK&=!checkAlmostLocked(A[0])
	if(isOK)isOK&=!checkAlmostLocked(A[1])
	if(isOK)isOK&=!checkAlmostLocked(A[2])
	return !isOK
}

function checkAlmostLocked(Atype){

	//here we check k by k for disjoint A and B with additional k in block, row, or column
	//all binary!
	var s=Atype.charAt(0)
	var st=""
	var isgrid=(s=="G")
	var type=(isgrid?Atype:s=="r"?"Row":s=="c"?"Col":"Block")	
	var A=[]
	var x=0
	var isOK=1
	var sindex1=0
	var sindex2=0


//logAddMessage(type+" "+ALSCatalog[type])

/*

first we look for


         X.......X
        /         \
   a---A           B---b
        \         /
         Y...*...Y
 

	* can be eliminated
*/


	for(var i=0;i<9;i++)for(var kpt=0;kpt<9;kpt++)if(ALSCatalog[type][i][kpt].length>1){
		A=ALSCatalog[type][i][kpt]
		//[sindex,rcblist,klist]
		//these are all weakly linked by the same kpt value in the same [row,col,block] i
		//for grids, these "kpt" are actually row/column numbers
		for(var s1=0;s1<A.length-1;s1++)for(var s2=s1+1;s2<A.length;s2++){
			sindex1 = A[s1][0]
			sindex2 = A[s2][0]
	

/*
if (sindex1.indexOf("43 ") >= 0 && sindex2.indexOf("101 ") >= 0)
alert(sindex1 + "\n" + sindex2
+ "\n\n" + AlmostLocked[sindex1][ALSWEAKNODES].join("\n") 
+ "\n\n" + AlmostLocked[sindex2][ALSWEAKNODES].join("\n") 
+ "\n\n " + A[s1][3] + " " + A[s2][3]
 )
*/

			
			if(xIsDisjointArray(AlmostLocked[sindex1][ALSDATACELLCHECK],AlmostLocked[sindex2][ALSDATACELLCHECK])){
				addAlmostLockedWeakLink(sindex1,sindex2,kpt, A[s1][3], A[s2][3])
				x=(A[s1][2]&A[s2][2])^Pwr2[kpt]  //some OTHER common value
				if(x){
					isOK&=!checkAlmostLockedNodes(x,sindex1,sindex2,kpt,isgrid)
					if(!solving&&!isOK)return 1
				}
			}
/*			
if (sindex1.indexOf("43 ") >= 0 && sindex2.indexOf("101 ") >= 0)
alert(sindex1 + "\n" + sindex2
+ "\n\n" + AlmostLocked[sindex1][ALSWEAKNODES].join("\n") 
+ "\n\n" + AlmostLocked[sindex2][ALSWEAKNODES].join("\n") 
+ "\n\n " + A[s1][3] + " " + A[s2][3]
 )
*/
		}
	}
	if(solving && !isOK)return 1
/*

here we are checking for 


         X.......X
        /         \
   a---A           B---b
        \         /
         Y.......Y

 
both a and b must be TRUE, so ALL associated weak nodes are FALSE

*/

//alert(dumpALSWeakLinks())

	var A
	//index here is PAIRS of sindex
	for(var ss in ALSWeakLinks) {
	//[sindex1,sindex2,xK,nK]
  	  if((A=ALSWeakLinks[ss])[3]>1){
		//eliminate all but those weak links
		isOK&=!eliminateAlmostLockedWeakLink(A)

		//force all SINGLE-cell nodes TRUE
		isOK&=!forceAlmostLockedTrue(A,0)
		isOK&=!forceAlmostLockedTrue(A,1)

		if(!solving&&!isOK)return 1
	  }
	}

	return !isOK
}

function checkAlmostLockedNodes(x,sindex1,sindex2,kpt,isgrid){
	//x is to be excluded from kpt (k or row/column pointer)
	var slist="ALS " + sindex1+" and ALS "+sindex2
	var N1=AlmostLocked[sindex1][ALSWEAKNODES] //[[C.showinfo,kreal,C,kpt],[C.showinfo,kreal,C,kpt],...]
	var N2=AlmostLocked[sindex2][ALSWEAKNODES]
	var s=""
	var isOK=1

/*

here we are checking for 


         kpt.....kpt
        /         \
   a---A           B---b
        \         /
         x...*...x
 

	* can be eliminated

we already have kpt as a link; we are now going for other common links log2(x)
*/


	for(var n1=0;n1<N1.length;n1++){
		var W1 = N1[n1]
		var C1info=W1[0]
		var kpt1=W1[3]
		// at least for now we are not using transmitted nodes
		if((x&Pwr2[kpt1]) == 0 || slist.indexOf(C1info)>=0  || W1[4].indexOf("via") >= 0) continue
		for(var n2=0;n2<N2.length;n2++){
			var W2 = N2[n2]
			if(C1info!=W2[0] || W1[1] != W2[1] || W2[4].indexOf("via") >= 0) continue // cell or k not the same
			s="weakly linked to two almost-locked sets already weakly linked "+(isgrid? "at row/column " : "by ")+(kpt+1)+": "+slist
			if(redlist.indexOf(C1info)<0){
				isOK=!eliminateK(W1[2],W1[1],s)
				showAlsChain(sindex1, sindex2)
				greenlist+=sindex1 + "ALSPOINT1"
				bluelist+=sindex2
				addAutoMsg("A1w",coordOf(W1[2],W1[1])+" is "+s)
				if(!solving && !isOK)return 1
			}
		}
	}
	return 0
}

function eliminateAlmostLockedWeakLink(A){
	//[sindex1,sindex2,xK,nK]
	var sindex1=A[0]
	var sindex2=A[1]
        var x = A[2] //all weak links
	var A12 = [sindex1,sindex2]
	var ifound=0
	var S=0
	var Links=[]
	var slist=sindex1 + sindex2
	var msg

	for(var p=0;p<2;p++){
		Links=AlmostLocked[A12[p]][ALSWEAKNODES] // [C.showinfo,kreal,C,kpt]
		for(var i=0;i<Links.length;i++)if((x&Pwr2[Links[i][3]])==0 && slist.indexOf(Links[i][0])<0){
			msg = "weak link to subset composed of almost-locked sets "+ sindex1 +" and "+sindex2+" mutually doubly-linked by "+xStr(x)
			ifound|=eliminateK(Links[i][2],Links[i][1],msg)
			showAlsChain(sindex1, sindex2)
		}
	}
	if(ifound){
		greenlist+=sindex1 + "ALSPOINT4"
		bluelist+=sindex2
		addAutoMsg("A1m",msg)
	}
	return ifound
}

function forceAlmostLockedTrue(A, pt){
	//[sindex1,sindex2,xK,nK]
	var sindex1 = A[pt]
	var sindex2 = A[1-pt]
	var isgrid=(AlmostLocked[sindex1][ALSGRIDTYPE] == 1)
	var ifound=0
	var SingleK=AlmostLocked[sindex1][ALSDATASINGLEK]
	var n=0
	var xlist=0
	var kpt=(isgrid?2:1)

//if(sindex1.indexOf("183 ")>=0)alert(SingleK)

	//only looking for single-position nodes here

	var x = 0x1FF ^ A[2] // all but mutual links

	for(var k=0;k<9;k++)if(x&Pwr2[k] && SingleK[k]){
		var ifound=0
		var C = SingleK[k]
		var p=Possible.XCell[C.row][C.col]^Pwr2[k]
		for(var k1=0;k1<9;k1++)if(p&Pwr2[k1]){
			var msg = "single-cell node " + C.showinfo + " must be "+(k+1)
			  +" for subset composed of almost-locked sets "
			  + AlmostLocked[sindex1][ALSID]+" and "+AlmostLocked[sindex2][ALSID]
			  +" mutually doubly-linked by "+xStr(A[2])
			ifound|=eliminateK(C,k1,msg)
			showAlsChain(sindex1, sindex2)
		}
		if (ifound){
			greenlist+=sindex1 + "ALSPOINT2"
			bluelist+=sindex2
			addAutoMsg("A1f",msg)
		}
	}
	return ifound
}

function showAlsChain(sindex1, sindex2) {
if (sindex1.indexOf("#") >= 0)
	ishowchain = -AlmostLocked[sindex1][ALSINDEX]
if (sindex2 && 	sindex2.indexOf("#") >= 0)
	ishowchain2 = -AlmostLocked[sindex2][ALSINDEX]
}
////////////// --- A2 --- at weak analysis time

//test: file://localhost/E:/js/sudoku/index.htm?MARKS=[[1,4],2,3,7,5,9,[1,4,8],[1,4,8],6,8,7,[1,4],2,6,[1,3,4],5,9,[1,3,4],9,[5,6],[5,6],[1,3,8],[1,3,8],[1,3,4,8],7,2,[1,3,4],[2,6],[1,5,6],[1,2,5,6,8],[1,3,5,8],4,[1,3,8],9,7,[1,5,8],3,[1,4,5],7,[1,5,8],9,6,[1,4,8],[1,4,5,8],2,[1,4],9,[1,4,5,8],[1,5,8],2,7,[3,6],[3,6],[1,4,5,8],5,[1,3,6],[1,2,6,9],4,7,[1,3,8],[1,2,3,6,8],[1,3,6,8],[1,8,9],[6,7],[1,3,4,6],[1,4,6,9],[6,9],[1,3,8],2,[1,3,4,6,8],[1,3,4,5,6,8],[1,4,5,7,8,9],[2,6,7],8,[1,2,4,6,9],[6,9],[1,3],5,[1,2,3,4,6],[1,3,4,6],[1,4,7,9]]
//fails: file://localhost/E:/js/sudoku/index.htm?MARKS=[[2,5],[1,2,6,7],8,4,[2,5,7],[5,6,9],[1,2,6],3,[1,9],[2,4,5],[1,2,6,7],[1,4,7],3,[2,5,7],[5,6,9],[1,2,6,8],[1,6,8],[1,8,9],9,[2,3,6],[2,3,6],[2,6,8],[2,8],1,5,7,4,7,9,[2,5,6],[1,2,5,6],[1,2,4,5],8,[1,3,6],[1,4,6],[1,3],[2,3,8],[2,3,6,8],[2,3,6],[1,2,6],[1,2,4],7,9,[1,4,6],5,1,4,[5,6],9,[3,5],[3,5,6],[7,8],2,[7,8],[3,4,8],[1,3,7,8],9,[1,5,7,8],6,[3,4,5],[1,3,7,8],[1,8],2,6,5,[1,7],[1,7,8],[1,3,8],2,4,9,[1,3,7,8],[2,3,4,8],[1,2,3,7,8],[1,4,7],[1,7,8],9,[3,4],[1,3,7,8],5,6]


globalWeakList = ""

function addALSWeakChainNode(coord,ALSNode,ichain,k,iparity,A,iskipcheck){
 //called by getWeakNodes() in strong.js
 // iparity = 1 --> chain node is 0
 // iparity = -1 --> chain node is 1

	var isOK=1
	var sindex=ALSNode[0]
	var kpt=ALSNode[2]
//logAddMessage("<br><br>addALSWeakChainNode: the chain " + ichain + " node " + coord + " has kpt=" + kpt + " for " + sindex)

 
	var myWeakNodes=AlmostLocked[sindex][ALSWEAKNODES]
/*
         w
         |
     x---A---k ...ichain(iparity)
         |
         z....a

	add all weak nodes "a" of A!k to strong chain as weak nodes, because if "a" is True, then
        z is false, and k must be True. 
        

 eliminations may occur two ways, just as for other chains, from addWeakNode:

         w
         |
     x---A---k ...ichain(0)
         |        
         z........ichain(1)

	weak links to w and x can be eliminated (forced FALSE by either possible chain state)

and

         w
         |
     x---A---k ...ichain(0)
         |        :
         z.........

	ichain(0) can be eliminated (both k and z cannot both be FALSE)

*/


	for(var i=0;i<myWeakNodes.length;i++){
		if(myWeakNodes[i][3]!=kpt){

			var k = myWeakNodes[i][1]
			var D = myWeakNodes[i][2]
			var type = myWeakNodes[i][4]

//logAddMessage(coord +" is a chain node that is a weak node with kpt=" + kpt + " but " + D.showinfo+"#"+(k+1) + " is a weak node with kpt=" + WeakNodes[i][3] + " of " + sindex +  " and so we add this weak node to the chain")

			var info = D.showinfo+"#"+(k+1) + " to " + chainCode(ichain,iparity,1)
			if (globalWeakList.indexOf(info) < 0) {
				globalWeakList += info
				if(ishowdetail)logAddMessage("adding weak node "+info+" based on ALS "+sindex)
				isOK&=addWeakNode(D,ichain,k,iparity,type + " -- via an almost-locked set (strong chain node " + coord + " forces ALS" + sindex + " to be everything but value " + (kpt + 1) + ", so all weak nodes of other values of that ALS are now weak nodes of the chain as well.)" ,A,0,iskipcheck)
				greenlist = type + sindex
			}
		}
		if(!isOK)break
	}
	return isOK
}

function checkWeakAlmostLocked(iskipcheck){

	//deprecated -- these are redundant
/*

         w
         |
     X---A---k ...ichain(0)
         |        :
         z.........

	ichain(0) can be eliminated

         w
         |
     X---A---k ...ichain(0)
         |   .   
         z   ........ichain(1)

	k can be eliminated


         w
         |
     X---A---k ...ichain(0)
         |        
         z........ichain(1)

*/
	return 0
}

function alsGetAssociatedNodes(ials){
	var W=[]
	var coord=""
	var List=[]
	var N = AlmostLocked[AlsChains[ials]][ALSWEAKNODES]
	for(var i=0;i<N.length;i++){
		var C = N[i][2]
		var k = N[i][1]
		coord=coordOf(C, k)
		List[List.length]=coord + (N[i][4].indexOf("via") >= 0 ? 2:1)
	}
	return List
}

function alsGetAssociatedNodeSet(ials){
	var List = alsGetAssociatedNodes(ials)
	return List.join(",")
}

function alsGetChainListing(ials){
	return AlmostLocked[AlsChains[ials]][ALSNODES]
}

function alsGetChainInfo(ials){
	return AlsChains[ials]
}


// detail

function dumpAlsData(xData) {
  var s="\n"
  for (var i = 0; i < 9; i++)if(xData.Nk[i]){
	s +="xData.Nk["+i+"]:" + xData.Nk[i] 
	+" xRk="+xStr(xData.xRk[i]) 
	+" xCk="+xStr(xData.xCk[i])
	+" xBk="+xStr(xData.xBk[i])
	+" xPk="+xStr(xData.xPk[i]) +"\n"
  }
  return s
}

function dumpALSWeakNodes(){
	for(var s in ALSWeakNodes)logAddMessage(s+" "+ALSWeakNodes[s]+" "+AlmostLocked[ALSWeakNodes[s][0][0]])
}

function dumpALSWeakLinks(){
	var s = ""
	for(var si in ALSWeakLinks)if (ALSWeakLinks[si][3]>1)s+="\n"+ALSWeakLinks[si]
	return s
}

// utilities

function xCreateAlsData(){
	A = {}
	A.Nk=[0,0,0,0,0,0,0,0,0]
	A.SingleK=[0,0,0,0,0,0,0,0,0]
	A.xRk=[0,0,0,0,0,0,0,0,0]
	A.xCk=[0,0,0,0,0,0,0,0,0]
	A.xBk=[0,0,0,0,0,0,0,0,0]
	A.xPk=[0,0,0,0,0,0,0,0,0]
	A.xCellCheck=[0,0,0,0,0,0,0,0,0]
	return A
}

function xIsDisjointArray(x1,x2){
	for (var i = 0; i < 9; i++)if (x1[i] & x2[i]) return 0
	return 1
}
