## Re: [xsl] Sudoku solver - complete

 Subject: Re: [xsl] Sudoku solver - complete From: "Dimitre Novatchev" Date: Tue, 2 May 2006 08:59:16 -0700
Wow...

Congratulations to Andrew for doing a fantastic speedup.

I will use this occasion to give you my latest two solutions
(sudoku4.xsl and sudoku5.xsl). From these the latter is a little bit
more sophisticated and works slightly faster when solving very
difficult boards.

My comparisons of Andrew's latest solution vs the two I provide below,
shows that there are cases where Andrew's solution outperforms mine up
to 4-5 times, other cases where they have the same time and still
other, where the two solutions below are 2-3 times faster.

Clearly, this is a valuable result, because it shows that both
Andrew's and mine sudoku solutions have a significant space for
improvement.

Therefore, wait for the next generation... :o)

I think that at this moment we have already improved our speed
hundreds of times.

Here are my two sudoku solvers:

sudoku4.xsl:
=========
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:xs="http://www.w3.org/2001/XMLSchema";
xmlns:f="http://fxsl.sf.net/";
exclude-result-prefixes="f xs">

<xsl:import href="func-apply.xsl"/>

<xsl:output omit-xml-declaration="yes" indent="yes"/>

<xsl:template match="/">

<xsl:variable name="vFixed" select="f:cellsGroup('Fixed', /*/*)"/>
<xsl:variable name="vEmpty" select="f:cellsGroup('Empty', /*/*)"/>

<xsl:variable name="vallFillers" as="element()*">
<xsl:for-each select=
"f:makeFillers(f:cellsGroup('Fixed', /*/*), f:cellsGroup('Empty',
/*/*))">
<xsl:sort select="@row"/>
<xsl:sort select="@col"/>

<xsl:sequence select="."/>
</xsl:for-each>
</xsl:variable>

<xsl:sequence
select="f:sudoku(\$vFixed, \$vEmpty, \$vallFillers)"/>
</xsl:template>

<xsl:variable name="vAllVals" select="1 to 9" as="xs:integer+"/>

<xsl:function name="f:cellsGroup" as="element()*">
<xsl:param name="pgrpType" as="xs:string"/>
<xsl:param name="pRows" as="element()*"/>
<xsl:sequence select=
"for \$i in 1 to count(\$pRows),
\$vRow in \$pRows[\$i],
\$vNum in tokenize(\$vRow, ',')
[if(\$pgrpType='Fixed')
then . ne '0'
else . eq '0'
][if(\$pgrpType='Empty')
then 1
else true()],
\$k in index-of(tokenize(\$vRow, ','),\$vNum)
return
f:makeCell(\$i,\$k, xs:integer(\$vNum))
"/>
</xsl:function>

<xsl:function name="f:makeCell" as="element()">
<xsl:param name="pnRow" as="xs:integer"/>
<xsl:param name="pnCol" as="xs:integer"/>
<xsl:param name="pnVal" as="xs:integer"/>

<cell row="{\$pnRow}" col="{\$pnCol}" val="{\$pnVal}"/>
</xsl:function>

<xsl:function name="f:sudoku" as="item()*">
<xsl:param name="pFixedCells" as="element()*"/>
<xsl:param name="pEmptyCells" as="element()*"/>
<xsl:param name="pallFillers" as="element()*"/>

<xsl:choose>
<xsl:when test="empty(\$pEmptyCells)">
<xsl:sequence select="f:printBoard(\$pFixedCells)"/>
</xsl:when>
<xsl:otherwise>

<xsl:variable name="vreducedFillers" select=
"f:reduceFillersByRules(\$pallFillers)"
/>

<xsl:if test="f:canContinue(\$pFixedCells, \$pEmptyCells,
\$vreducedFillers)">
<xsl:variable name="vBestCell" select=
"f:bestCellToTry(\$pEmptyCells,\$vreducedFillers)"/>
<xsl:variable name="vcellFillers" as="element()*"
select="f:cellFillers(\$vreducedFillers,\$vBestCell)"/>

<xsl:sequence select=
"f:tryFillers(\$pFixedCells,\$pEmptyCells, \$vreducedFillers,
\$vcellFillers,\$vBestCell)"
/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:function>

<xsl:function name="f:canContinue" as="xs:boolean">
<xsl:param name="pFixedCells" as="element()*"/>
<xsl:param name="pEmptyCells" as="element()*"/>
<xsl:param name="pallFillers" as="element()*"/>

<xsl:variable name="vallSingleFillers" as="element()*" select=
"f:getSingleFillers(\$pallFillers, \$pEmptyCells)"
/>
<xsl:sequence select=
"if(empty(
for \$i in 1 to 9
return
\$i[f:hasDuplicates(
f:row(\$vallSingleFillers,\$i)/@val
)
or
f:hasDuplicates(
f:column(\$vallSingleFillers,\$i)/@val
)
or
f:hasDuplicates(
f:staticRegion(\$vallSingleFillers,\$i)/@val
)
]
)
)
then
true() (:
empty(\$pEmptyCells[empty(f:cellFillers(\$pallFillers,.))]) :)
else
false()

"/>
<!-- -->
</xsl:function>

<xsl:function name="f:getSingleFillers" as="element()*">
<xsl:param name="pallFillers" as="element()*"/>
<xsl:param name="pEmptyCells" as="element()*"/>

<xsl:for-each select="\$pEmptyCells">
<xsl:variable name="vtheFillers"
select="f:cellFillers(\$pallFillers,.)"/>

<xsl:sequence select=
"if(not(\$vtheFillers[2]))
then \$vtheFillers[1]
else ()
"
/>
</xsl:for-each>
</xsl:function>

<xsl:function name="f:hasDuplicates" as="xs:boolean">
<xsl:param name="pSeq" as="item()*"/>

<xsl:sequence select="count(distinct-values(\$pSeq)) ne count(\$pSeq)"/>

</xsl:function>

<xsl:function name="f:cellFillers" as="element()*">
<xsl:param name="pallFillers" as="element()*"/>
<xsl:param name="pemptyCell" as="element()"/>

<xsl:sequence select="\$pallFillers[@row eq \$pemptyCell/@row
and
@col eq \$pemptyCell/@col
]"/>

</xsl:function>

<xsl:function name="f:bestCellToTry" as="element()?">
<xsl:param name="pEmptyCells" as="element()*"/>
<xsl:param name="pallFillers" as="element()*"/>

<xsl:for-each-group select="\$pEmptyCells"
group-by="count(f:cellFillers(\$pallFillers,.))">
<xsl:sort select="current-grouping-key()" order="ascending"/>

<xsl:if test="position() = 1">
<xsl:sequence select="current-group()[1]"/>
</xsl:if>
</xsl:for-each-group>
</xsl:function>

<xsl:function name="f:makeFillers" as="element()*">
<xsl:param name="pFixedCells" as="element()*"/>
<xsl:param name="pEmptyCells" as="element()*"/>

<xsl:sequence select="\$pEmptyCells/f:makeFillersForCell(\$pFixedCells,.)"/>
</xsl:function>

<xsl:function name="f:makeFillersForCell" as="element()*">
<xsl:param name="pFixedCells" as="element()*"/>
<xsl:param name="pEmptyCell" as="element()"/>

<xsl:for-each select="\$vAllVals">
<xsl:if test="not(. = f:column(\$pFixedCells,\$pEmptyCell/@col)/@val)
and
not(. = f:row(\$pFixedCells,\$pEmptyCell/@row)/@val)
and
not(. = f:region(\$pFixedCells, \$pEmptyCell)/@val)
">
<xsl:sequence
select="f:makeCell(\$pEmptyCell/@row,\$pEmptyCell/@col,.)"/>
</xsl:if>
</xsl:for-each>
</xsl:function>

<xsl:function name="f:tryFillers" as="item()*">
<xsl:param name="pFixedCells" as="element()*"/>
<xsl:param name="pEmptyCells" as="element()*"/>
<xsl:param name="pallFillers" as="element()*"/>
<xsl:param name="pFillers" as="element()*"/>
<xsl:param name="pBestCell" as="element()"/>

<xsl:if test="exists(\$pFillers)">
<xsl:variable name="vtheFiller" select="\$pFillers[1]"/>

<xsl:variable name="vreducedFilllers" as="element()*"
select="f:reduceAllFillers(\$pallFillers, \$pBestCell,\$vtheFiller)"/>

<xsl:variable name="vSolution" select=
"f:sudoku((\$pFixedCells,\$vtheFiller),\$pEmptyCells[not(. is \$pBestCell)],
\$vreducedFilllers
)
"/>

<xsl:choose>
<xsl:when test="exists(\$vSolution)">
<xsl:sequence select="\$vSolution"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select=
"f:tryFillers(\$pFixedCells,\$pEmptyCells, \$pallFillers,
subsequence(\$pFillers,2),\$pBestCell)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:function>

<xsl:function name="f:reduceFillersByRules" as="element()*">
<xsl:param name="pFillers" as="element()*"/>

<xsl:variable name="vSingleFillers" as="element()*">
<xsl:for-each select="f:row(), f:column(), f:staticRegion()">
<xsl:variable name="vcurgroupFun" select="."/>

<xsl:for-each select="1 to 9">
<xsl:variable name="vthisGroupFillers"
select="f:apply(\$vcurgroupFun,\$pFillers, .)"/>

<xsl:for-each-group select="\$vthisGroupFillers"
group-by="@val"
>
<xsl:sort select="count(current-group())"/>

<xsl:if test="not(current-group()[2])">
<xsl:sequence select="current-group()[1]"/>
</xsl:if>
</xsl:for-each-group>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>

<xsl:sequence select=
"for \$filler in \$pFillers
return
if(not(exists(\$vSingleFillers
[not(. is \$filler)
and
@row=\$filler/@row
and
@col=\$filler/@col
]
)
)
)
then \$filler
else ()
"/>
</xsl:function>

<xsl:function name="f:reduceAllFillers" as="element()*">
<xsl:param name="pallFillers" as="element()*"/>
<xsl:param name="ppickedCell" as="element()"/>
<xsl:param name="pthisFiller" as="element()"/>

<xsl:sequence select=
"\$pallFillers[(@row ne \$ppickedCell/@row
or
@col ne \$ppickedCell/@col
)
and
(
if(@row = \$ppickedCell/@row
or
@col = \$ppickedCell/@col
or
(
((@row - 1) idiv 3 eq (\$ppickedCell/@row -1) idiv 3)
and
((@col - 1) idiv 3 eq (\$ppickedCell/@col -1) idiv 3)
)
)
then
@val ne \$pthisFiller/@val
else
true()
)
]
"
/>
</xsl:function>

<xsl:variable name="vfunRow" as="element()">
<f:row/>
</xsl:variable>

<xsl:template match="f:row" mode="f:FXSL" as="element()*">
<xsl:param name="arg1" as="element()*"/>
<xsl:param name="arg2" as="xs:integer"/>

<xsl:sequence select="f:row(\$arg1,\$arg2)"/>
</xsl:template>

<xsl:function name="f:row" as="element()">
<xsl:sequence select="\$vfunRow"/>
</xsl:function>

<xsl:variable name="vfunColumn" as="element()">
<f:column/>
</xsl:variable>

<xsl:template match="f:column" mode="f:FXSL" as="element()*">
<xsl:param name="arg1" as="element()*"/>
<xsl:param name="arg2" as="xs:integer"/>

<xsl:sequence select="f:column(\$arg1,\$arg2)"/>
</xsl:template>

<xsl:function name="f:column" as="element()">
<xsl:sequence select="\$vfunColumn"/>
</xsl:function>

<xsl:variable name="vfunStatRegion" as="element()">
<f:staticRegion/>
</xsl:variable>

<xsl:template match="f:staticRegion" mode="f:FXSL" as="element()*">
<xsl:param name="arg1" as="element()*"/>
<xsl:param name="arg2" as="xs:integer"/>

<xsl:sequence select="f:staticRegion(\$arg1,\$arg2)"/>
</xsl:template>

<xsl:function name="f:staticRegion" as="element()">
<xsl:sequence select="\$vfunStatRegion"/>
</xsl:function>

<xsl:function name="f:column" as="element()*">
<xsl:param name="pCells" as="element()*"/>
<xsl:param name="pColno" as="xs:integer"/>

<xsl:sequence select="\$pCells[@col = \$pColno]"/>
</xsl:function>

<xsl:function name="f:row" as="element()*">
<xsl:param name="pCells" as="element()*"/>
<xsl:param name="pRowno" as="xs:integer"/>

<xsl:sequence select="\$pCells[@row = \$pRowno]"/>
</xsl:function>

<xsl:function name="f:region" as="element()*">
<xsl:param name="pCells" as="element()*"/>
<xsl:param name="paCell" as="element()"/>

<xsl:variable name="vregRowStart" as="xs:integer"
select="3*((\$paCell/@row -1) idiv 3) +1"/>
<xsl:variable name="vregColStart" as="xs:integer"
select="3*((\$paCell/@col -1) idiv 3) +1"/>

<xsl:sequence select=
"\$pCells[xs:integer(@row) ge \$vregRowStart and xs:integer(@row)
lt (\$vregRowStart +3)
and
xs:integer(@col) ge \$vregColStart and xs:integer(@col) lt
(\$vregColStart +3)
]"/>
</xsl:function>

<xsl:variable name="vRegdata" as="element()+">
<reg rstart="1" colstart="1"/>
<reg rstart="1" colstart="4"/>
<reg rstart="1" colstart="7"/>
<reg rstart="4" colstart="1"/>
<reg rstart="4" colstart="4"/>
<reg rstart="4" colstart="7"/>
<reg rstart="7" colstart="1"/>
<reg rstart="7" colstart="4"/>
<reg rstart="7" colstart="7"/>
</xsl:variable>

<xsl:function name="f:staticRegion" as="element()*">
<xsl:param name="pCells" as="element()*"/>
<xsl:param name="pRegNo" as="xs:integer"/>

<xsl:variable name="vregRowStart" as="xs:integer?"
select="xs:integer(\$vRegdata[\$pRegNo]/@rstart)"/>
<xsl:variable name="vregColStart" as="xs:integer?"
select="xs:integer(\$vRegdata[\$pRegNo]/@colstart)"/>

<xsl:sequence select=
"\$pCells[xs:integer(@row) ge \$vregRowStart and xs:integer(@row) lt
(\$vregRowStart + 3)
and
xs:integer(@col) ge \$vregColStart and xs:integer(@col) lt
(\$vregColStart + 3)
]"
/>

</xsl:function>

<xsl:function name="f:printBoard">
<xsl:param name="pCells" as="element()+"/>

<xsl:for-each-group select="\$pCells" group-by="@row">
<xsl:sort select="current-grouping-key()"/>
<row>
<xsl:for-each select="current-group()">
<xsl:sort select="@col"/>
<xsl:value-of select=
"concat(@val, if(position() ne last()) then ', ' else ())"
/>
</xsl:for-each>
</row>
</xsl:for-each-group>
</xsl:function>
</xsl:stylesheet>

sudoku5.xsl: ========= <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; xmlns:xs="http://www.w3.org/2001/XMLSchema"; xmlns:f="http://fxsl.sf.net/"; exclude-result-prefixes="f xs">

<xsl:import href="func-apply.xsl"/>

<xsl:output omit-xml-declaration="yes" indent="yes"/>

<xsl:template match="/">

<xsl:variable name="vFixed" select="f:cellsGroup('Fixed', /*/*)"/>
<xsl:variable name="vEmpty" select="f:cellsGroup('Empty', /*/*)"/>

<xsl:variable name="vallFillers" as="element()*">
<xsl:for-each select=
"f:makeFillers(f:cellsGroup('Fixed', /*/*), f:cellsGroup('Empty',
/*/*))">
<xsl:sort select="@row"/>
<xsl:sort select="@col"/>

<xsl:sequence select="."/>
</xsl:for-each>
</xsl:variable>

<xsl:sequence
select="f:sudoku(\$vFixed, \$vEmpty, \$vallFillers)"/>
</xsl:template>

<xsl:variable name="vAllVals" select="1 to 9" as="xs:integer+"/>

<xsl:function name="f:cellsGroup" as="element()*">
<xsl:param name="pgrpType" as="xs:string"/>
<xsl:param name="pRows" as="element()*"/>
<xsl:sequence select=
"for \$i in 1 to count(\$pRows),
\$vRow in \$pRows[\$i],
\$vNum in tokenize(\$vRow, ',')
[if(\$pgrpType='Fixed')
then . ne '0'
else . eq '0'
][if(\$pgrpType='Empty')
then 1
else true()],
\$k in index-of(tokenize(\$vRow, ','),\$vNum)
return
f:makeCell(\$i,\$k, xs:integer(\$vNum))
"/>
</xsl:function>

<xsl:function name="f:makeCell" as="element()">
<xsl:param name="pnRow" as="xs:integer"/>
<xsl:param name="pnCol" as="xs:integer"/>
<xsl:param name="pnVal" as="xs:integer"/>

<cell row="{\$pnRow}" col="{\$pnCol}" val="{\$pnVal}"/>
</xsl:function>

<xsl:function name="f:sudoku" as="item()*">
<xsl:param name="pFixedCells" as="element()*"/>
<xsl:param name="pEmptyCells" as="element()*"/>
<xsl:param name="pallFillers" as="element()*"/>

<xsl:choose>
<xsl:when test="empty(\$pEmptyCells)">
<xsl:sequence select="f:printBoard(\$pFixedCells)"/>
</xsl:when>
<xsl:otherwise>

<xsl:if test="f:canContinue(\$pFixedCells, \$pEmptyCells, \$pallFillers)">
<xsl:variable name="vBestCell" select=
"f:bestCellToTry(\$pEmptyCells,\$pallFillers)"/>

<xsl:variable name="vcellFillers" as="element()*"
select="f:cellFillers(\$pallFillers,\$vBestCell)"/>

<xsl:sequence select=
"f:tryFillers(\$pFixedCells,\$pEmptyCells, \$pallFillers,
\$vcellFillers,\$vBestCell)"
/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:function>

<xsl:function name="f:canContinue" as="xs:boolean">
<xsl:param name="pFixedCells" as="element()*"/>
<xsl:param name="pEmptyCells" as="element()*"/>
<xsl:param name="pallFillers" as="element()*"/>

<xsl:variable name="vallSingleFillers" as="element()*" select=
"f:getSingleFillers(\$pallFillers, \$pEmptyCells)"
/>
<xsl:sequence select=
"if(empty(
for \$i in 1 to 9
return
\$i[f:hasDuplicates(
f:row(\$vallSingleFillers,\$i)/@val
)
or
f:hasDuplicates(
f:column(\$vallSingleFillers,\$i)/@val
)
or
f:hasDuplicates(
f:staticRegion(\$vallSingleFillers,\$i)/@val
)
]
)
)
then
true() (:
empty(\$pEmptyCells[empty(f:cellFillers(\$pallFillers,.))]) :)
else
false()

"/>
</xsl:function>

<xsl:function name="f:getSingleFillers" as="element()*">
<xsl:param name="pallFillers" as="element()*"/>
<xsl:param name="pEmptyCells" as="element()*"/>

<xsl:for-each select="\$pEmptyCells">
<xsl:variable name="vtheFillers"
select="f:cellFillers(\$pallFillers,.)"/>

<xsl:sequence select=
"if(not(\$vtheFillers[2]))
then \$vtheFillers[1]
else ()
"
/>
</xsl:for-each>
</xsl:function>

<xsl:function name="f:hasDuplicates" as="xs:boolean">
<xsl:param name="pSeq" as="item()*"/>

<xsl:sequence select="count(distinct-values(\$pSeq)) ne count(\$pSeq)"/>

</xsl:function>

<xsl:function name="f:cellFillers" as="element()*">
<xsl:param name="pallFillers" as="element()*"/>
<xsl:param name="pemptyCell" as="element()"/>

<xsl:sequence select="\$pallFillers[@row eq \$pemptyCell/@row
and
@col eq \$pemptyCell/@col
]"/>

</xsl:function>

<xsl:function name="f:bestCellToTry" as="element()?">
<xsl:param name="pEmptyCells" as="element()*"/>
<xsl:param name="pallFillers" as="element()*"/>

<xsl:for-each-group select="\$pEmptyCells"
group-by="count(f:cellFillers(\$pallFillers,.))">
<xsl:sort select="current-grouping-key()" order="ascending"/>

<xsl:if test="position() = 1">
<xsl:sequence select="current-group()[1]"
/>
</xsl:if>
</xsl:for-each-group>
</xsl:function>

<xsl:function name="f:makeFillers" as="element()*">
<xsl:param name="pFixedCells" as="element()*"/>
<xsl:param name="pEmptyCells" as="element()*"/>

<xsl:sequence select="\$pEmptyCells/f:makeFillersForCell(\$pFixedCells,.)"/>
</xsl:function>

<xsl:function name="f:makeFillersForCell" as="element()*">
<xsl:param name="pFixedCells" as="element()*"/>
<xsl:param name="pEmptyCell" as="element()"/>

<xsl:for-each select="\$vAllVals">
<xsl:if test="not(. = f:column(\$pFixedCells,\$pEmptyCell/@col)/@val)
and
not(. = f:row(\$pFixedCells,\$pEmptyCell/@row)/@val)
and
not(. = f:region(\$pFixedCells, \$pEmptyCell)/@val)
">
<xsl:sequence
select="f:makeCell(\$pEmptyCell/@row,\$pEmptyCell/@col,.)"/>
</xsl:if>
</xsl:for-each>
</xsl:function>

<xsl:function name="f:tryFillers" as="item()*">
<xsl:param name="pFixedCells" as="element()*"/>
<xsl:param name="pEmptyCells" as="element()*"/>
<xsl:param name="pallFillers" as="element()*"/>
<xsl:param name="pFillers" as="element()*"/>
<xsl:param name="pBestCell" as="element()"/>

<xsl:if test="exists(\$pFillers)">
<xsl:variable name="vtheFiller" select="\$pFillers[1]"/>

<xsl:variable name="vreducedFilllers" as="element()*"
select="f:reduceAllFillers(\$pallFillers, \$pBestCell,\$vtheFiller)"/>

<xsl:variable name="vreducedFillers2" select=
"f:reduceFillersByRules(\$vreducedFilllers)"
/>

<xsl:variable name="vSolution" select= "f:sudoku((\$pFixedCells,\$vtheFiller),\$pEmptyCells[not(. is \$pBestCell)], \$vreducedFillers2 ) "/>

<xsl:choose>
<xsl:when test="exists(\$vSolution)">
<xsl:sequence select="\$vSolution"/>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select=
"f:tryFillers(\$pFixedCells,\$pEmptyCells, \$pallFillers,
subsequence(\$pFillers,2),\$pBestCell)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:function>

<xsl:function name="f:reduceFillersByRules" as="element()*">
<xsl:param name="pFillers" as="element()*"/>

<!--f:reduceAllFillers(\$pallFillers, \$pBestCell,\$vtheFiller) -->

<xsl:variable name="vSingleValueFillers" as="element()*">
<xsl:for-each select="f:row(), f:column(), f:staticRegion()">
<xsl:variable name="vcurgroupFun" select="."/>

<xsl:for-each select="1 to 9">
<xsl:variable name="vthisGroupFillers"
select="f:apply(\$vcurgroupFun,\$pFillers, .)"/>

<xsl:for-each-group select="\$vthisGroupFillers"
group-by="@val"
>
<xsl:sort select="count(current-group())"/>

<xsl:if test="not(current-group()[2])">
<xsl:sequence select="current-group()[1]"/>
</xsl:if>
</xsl:for-each-group>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>

<xsl:variable name="vreducedFillers2" as="element()*"
select=
"for \$filler in \$pFillers
return
if(not(exists(\$vSingleValueFillers
[not(. is \$filler)
and
@row=\$filler/@row
and
@col=\$filler/@col
]
)
)
)
then \$filler
else ()
"/>

<xsl:sequence select="\$vreducedFillers2"/>
<!--
<xsl:variable name="vSingleFillers" as="element()*">
<xsl:for-each-group select="\$vreducedFillers2"
group-by="concat(@row,@col)"
>
<xsl:sequence select=
"if(not(current-group()[2]))
then current-group()[1]
else ()
"/>
</xsl:for-each-group>
</xsl:variable>

<xsl:variable name="vreducedFillers1" as="element()*"
select=
"for \$filler in \$vreducedFillers2
return
if(not(exists(\$vSingleFillers
[not(. is \$filler)
and
@val = \$filler/@val
and
(@row=\$filler/@row
or
@col=\$filler/@col
or
( ((@row -1) idiv 3 eq (\$filler/@row -1) idiv 3)
and
((@col -1) idiv 3 eq (\$filler/@col -1) idiv 3)
)
)
]
)
)
)
then \$filler
else ()
"
/>
<xsl:sequence select="\$vreducedFillers1"/>
-->
<!--
<xsl:sequence select=
"if(count(\$vreducedFillers2) eq count(\$pFillers))
then
\$vreducedFillers2
else
f:reduceFillersByRules(\$vreducedFillers2)
"
/>
-->
</xsl:function>

<xsl:function name="f:reduceAllFillers" as="element()*">
<xsl:param name="pallFillers" as="element()*"/>
<xsl:param name="ppickedCell" as="element()"/>
<xsl:param name="pthisFiller" as="element()"/>

<xsl:sequence select=
"\$pallFillers[(@row ne \$ppickedCell/@row
or
@col ne \$ppickedCell/@col
)
and
(
if(@row = \$ppickedCell/@row
or
@col = \$ppickedCell/@col
or
(
((@row - 1) idiv 3 eq (\$ppickedCell/@row -1) idiv 3)
and
((@col - 1) idiv 3 eq (\$ppickedCell/@col -1) idiv 3)
)
)
then
@val ne \$pthisFiller/@val
else
true()
)
]
"
/>
</xsl:function>

<xsl:variable name="vfunRow" as="element()">
<f:row/>
</xsl:variable>

<xsl:template match="f:row" mode="f:FXSL" as="element()*">
<xsl:param name="arg1" as="element()*"/>
<xsl:param name="arg2" as="xs:integer"/>

<xsl:sequence select="f:row(\$arg1,\$arg2)"/>
</xsl:template>

<xsl:function name="f:row" as="element()">
<xsl:sequence select="\$vfunRow"/>
</xsl:function>

<xsl:variable name="vfunColumn" as="element()">
<f:column/>
</xsl:variable>

<xsl:template match="f:column" mode="f:FXSL" as="element()*">
<xsl:param name="arg1" as="element()*"/>
<xsl:param name="arg2" as="xs:integer"/>

<xsl:sequence select="f:column(\$arg1,\$arg2)"/>
</xsl:template>

<xsl:function name="f:column" as="element()">
<xsl:sequence select="\$vfunColumn"/>
</xsl:function>

<xsl:variable name="vfunStatRegion" as="element()">
<f:staticRegion/>
</xsl:variable>

<xsl:template match="f:staticRegion" mode="f:FXSL" as="element()*">
<xsl:param name="arg1" as="element()*"/>
<xsl:param name="arg2" as="xs:integer"/>

<xsl:sequence select="f:staticRegion(\$arg1,\$arg2)"/>
</xsl:template>

<xsl:function name="f:staticRegion" as="element()">
<xsl:sequence select="\$vfunStatRegion"/>
</xsl:function>

<xsl:function name="f:column" as="element()*">
<xsl:param name="pCells" as="element()*"/>
<xsl:param name="pColno" as="xs:integer"/>

<xsl:sequence select="\$pCells[@col = \$pColno]"/>
</xsl:function>

<xsl:function name="f:row" as="element()*">
<xsl:param name="pCells" as="element()*"/>
<xsl:param name="pRowno" as="xs:integer"/>

<xsl:sequence select="\$pCells[@row = \$pRowno]"/>
</xsl:function>

<xsl:function name="f:region" as="element()*">
<xsl:param name="pCells" as="element()*"/>
<xsl:param name="paCell" as="element()"/>

<xsl:variable name="vregRowStart" as="xs:integer"
select="3*((\$paCell/@row -1) idiv 3) +1"/>
<xsl:variable name="vregColStart" as="xs:integer"
select="3*((\$paCell/@col -1) idiv 3) +1"/>

<xsl:sequence select=
"\$pCells[xs:integer(@row) ge \$vregRowStart and xs:integer(@row)
lt (\$vregRowStart +3)
and
xs:integer(@col) ge \$vregColStart and xs:integer(@col) lt
(\$vregColStart +3)
]"/>
</xsl:function>

<xsl:variable name="vRegdata" as="element()+">
<reg rstart="1" colstart="1"/>
<reg rstart="1" colstart="4"/>
<reg rstart="1" colstart="7"/>
<reg rstart="4" colstart="1"/>
<reg rstart="4" colstart="4"/>
<reg rstart="4" colstart="7"/>
<reg rstart="7" colstart="1"/>
<reg rstart="7" colstart="4"/>
<reg rstart="7" colstart="7"/>
</xsl:variable>

<xsl:function name="f:staticRegion" as="element()*">
<xsl:param name="pCells" as="element()*"/>
<xsl:param name="pRegNo" as="xs:integer"/>

<xsl:variable name="vregRowStart" as="xs:integer?"
select="xs:integer(\$vRegdata[\$pRegNo]/@rstart)"/>
<xsl:variable name="vregColStart" as="xs:integer?"
select="xs:integer(\$vRegdata[\$pRegNo]/@colstart)"/>

<xsl:sequence select=
"\$pCells[xs:integer(@row) ge \$vregRowStart and xs:integer(@row) lt
(\$vregRowStart + 3)
and
xs:integer(@col) ge \$vregColStart and xs:integer(@col) lt
(\$vregColStart + 3)
]"
/>

</xsl:function>

<xsl:function name="f:printBoard">
<xsl:param name="pCells" as="element()+"/>

<xsl:for-each-group select="\$pCells" group-by="@row">
<xsl:sort select="current-grouping-key()"/>
<row>
<xsl:for-each select="current-group()">
<xsl:sort select="@col"/>
<xsl:value-of select=
"concat(@val, if(position() ne last()) then ', ' else ())"
/>
</xsl:for-each>
</row>
</xsl:for-each-group>
</xsl:function>
</xsl:stylesheet>

-- Cheers, Dimitre Novatchev --------------------------------------- Truly great madness cannot be achieved without significant intelligence.

On 5/2/06, andrew welch <andrew.j.welch@xxxxxxxxx> wrote:
For those of you interested in this I've now finished performance
tuning my Sudoku solution.

I'm quite pleased with it - boards that were taking 15mins+ now take
less than a few seconds.  The most pleasing aspect is the way it
selects the allowed values for a cell - it's much better at detecting
when there's only one possible value, and the way it works through the
board.

I'm also making much more use of subsequence() - several seconds were
shaved off after constructing new sequences from existings sequences
using this function.

I'm satisfied that it's complete now, but if anyone can improve it or
break it, please let me know

It's available here: http://ajwelch.blogspot.com/

cheers
andrew