<!-- n-queens.xsl disguised as XML file for the browsers, for $N <= 9 -->

<!-- Valery Chernysh's .xsl.xml technique for execution in all browsers -->
<?xml-stylesheet href="#" type="text/xsl"?>

<!-- alternative over specifying input in data:data section -->
<!DOCTYPE xsl:stylesheet [ 
  <!ENTITY internalImages "false()"> 
  <!ENTITY minN "4"> 
  <!ENTITY maxN "99"> 
]>

<!-- this is the stylesheet being referenced by href="#" above -->
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exslt="http://exslt.org/common"
  xmlns:n-queens="urn:n-queens"
  xmlns:board="urn:board"
  xmlns:chess="urn:chess"
  exclude-result-prefixes="n-queens exslt"
>
<!-- find David Carlisle's  exslt:node-set() for IE browsers at bottom -->

<!-- 
     Pattern allowing repeated processing of produced node-set results:
       <xsl:variable name="blah0">...</xsl:variable>
       <xsl:variable name="blah" select="exslt:node-set($blah0)"/>
-->
  <xsl:output omit-xml-declaration="yes"/>


  <!-- entry point -->
  <xsl:template match="/xsl:stylesheet">
<html><body>
    <xsl:call-template name="n-queens:multi"/>
</body></html>
  </xsl:template>


  <!-- recursive $N-queens output for $N in [&minN;...&maxN;] -->
  <xsl:template name="n-queens:multi">
    <xsl:param name="N" select="&minN;"/>

    <!-- generate $Nx$N board -->
    <xsl:variable name="row0">
      <xsl:call-template name="n-queens:row">
        <xsl:with-param name="n" select="$N"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="row" select="exslt:node-set($row0)"/>

    <xsl:variable name="rows0">
      <xsl:for-each select="$row/*">
        <r><xsl:copy-of select="$row"/></r>
      </xsl:for-each>
    </xsl:variable>
    <xsl:variable name="rows" select="exslt:node-set($rows0)"/>

    <!-- determine all solutions of $N queens problem -->
    <xsl:variable name="sols0">
      <xsl:call-template name="n-queens:search">
        <xsl:with-param name="b" select="$rows/*"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="sols" select="exslt:node-set($sols0)"/>

    <!-- output all $N-queens solutions -->
<p/>
    <xsl:value-of select="count($sols/sol)"/>
    <xsl:text> distinct </xsl:text>
    <xsl:value-of select="$N"/>
    <xsl:text>-queens solution</xsl:text>
    <xsl:if test="count($sols/sol)>1">s</xsl:if>
    <xsl:text> (</xsl:text>
    <xsl:call-template name="n-queens:cntsum">
      <xsl:with-param name="cnts" select="$sols/sol"/>
    </xsl:call-template>
    <xsl:text> in total)</xsl:text>
<table border="1">
    <xsl:apply-templates select="$sols/sol"/>
</table>

    <!-- recursive call for next $N-queens problem -->
    <xsl:if test="&maxN; > $N and 9 > $N">
      <xsl:call-template name="n-queens:multi">
        <xsl:with-param name="N" select="$N + 1"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>


  <!-- recursive search for all solutions -->
  <xsl:template name="n-queens:search">    
    <xsl:param name="b"/>  <!-- remaining rows of not threatened fields -->
    <xsl:param name="s"/>  <!-- partial solution of queens fixated sofar -->

    <!-- complete board filled means solution found -->
    <xsl:if test="not($b)"> 

      <!-- determine all rotations and/or mirrors of found solution -->
      <xsl:variable name="sorted0">
        <xsl:call-template name="board:sorted">
          <xsl:with-param name="sola" select="$s"/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="sorted" select="exslt:node-set($sorted0)"/>

      <!-- take "minimal" solution among rotations and/or mirrors only -->
      <xsl:if test="$sorted/*[1] = $s">
        <sol cnt="{count($sorted/*)}"><xsl:value-of select="$s"/></sol> 
      </xsl:if>
    </xsl:if>

    <!-- check each remaining possible position in next row -->
    <xsl:for-each select="$b[1]/*">

      <!-- sieve out fields by new current (.) queen in current row -->
      <xsl:variable name="sieved0">
        <xsl:call-template name="n-queens:sieve">
          <xsl:with-param name="c" select="."/>
          <xsl:with-param name="b" select="$b[position()>1]"/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="sieved" select="exslt:node-set($sieved0)"/>

      <!-- recursive call -->
      <xsl:call-template name="n-queens:search">
        <xsl:with-param name="b" select="$sieved/*"/>
        <xsl:with-param name="s" select="concat($s, .)"/>
      </xsl:call-template>
    </xsl:for-each>
  </xsl:template>

  <!-- sieve out fields in remaining rows attacked by queen at column $c -->
  <xsl:template name="n-queens:sieve">    
    <xsl:param name="c"/>  <!-- column of newly fixed queen -->
    <xsl:param name="b"/>  <!-- remaining rows -->

    <xsl:for-each select="$b">
      <!-- row number for diagonal attack determination -->
      <xsl:variable name="r" select="position()"/>

      <!-- copy fields not vertically or diagonally attacked -->
      <r><xsl:copy-of select="*[. != $c][. - $r != $c][. + $r != $c]"/></r>
    </xsl:for-each>
  </xsl:template>

  <!-- determine sum(sol/@cnt) -->
  <xsl:template name="n-queens:cntsum">   
    <xsl:param name="cnts" select="/.."/>

    <xsl:if test="not($cnts)">0</xsl:if>

    <xsl:if test="$cnts">
      <xsl:variable name="rest">
        <xsl:call-template name="n-queens:cntsum">
          <xsl:with-param name="cnts" select="$cnts[position()>1]"/>
        </xsl:call-template>
      </xsl:variable>

      <xsl:value-of select="$cnts[1]/@cnt + $rest"/>
    </xsl:if> 
  </xsl:template>

  <!-- generate node-set of the form "<f>1</f><f>2</f>...<f>$n</f>" -->
  <xsl:template name="n-queens:row">    
    <xsl:param name="n"/>

    <xsl:if test="$n>0">
      <xsl:call-template name="n-queens:row">
        <xsl:with-param name="n" select="$n - 1"/>
      </xsl:call-template>

      <f><xsl:value-of select="$n"/></f>
    </xsl:if>
  </xsl:template>


  <!-- generate position by a 90 degree rotation -->
  <xsl:template name="board:rot90">
    <xsl:param name="sol"/>
    <xsl:param name="n" select="1"/>

    <xsl:variable name="len" select="string-length($sol)"/>

    <xsl:if test="$len >= $n">
      <xsl:value-of select="$len - string-length(substring-before($sol,$n))"/>

      <xsl:call-template name="board:rot90">
        <xsl:with-param name="sol" select="$sol"/>
        <xsl:with-param name="n" select="$n + 1"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <!-- generate position by vertical mirroring -->
  <xsl:template name="board:mirror">
    <xsl:param name="sol"/>
    <xsl:param name="n" select="string-length($sol)"/>

    <xsl:if test="$sol">
      <xsl:value-of select="1 + $n - substring($sol, 1, 1)"/>

      <xsl:call-template name="board:mirror">
        <xsl:with-param name="sol" select="substring($sol, 2)"/>
        <xsl:with-param name="n" select="$n"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <!-- determine unique rotations and/or mirrors for a given solution -->
  <xsl:template name="board:sorted">
    <xsl:param name="sola"/>

    <xsl:variable name="solA">
      <xsl:call-template name="board:mirror">
        <xsl:with-param name="sol" select="$sola"/>
      </xsl:call-template>
    </xsl:variable>

    <xsl:variable name="solb">
      <xsl:call-template name="board:rot90">
        <xsl:with-param name="sol" select="$sola"/>
      </xsl:call-template>
    </xsl:variable>

    <xsl:variable name="solB">
      <xsl:call-template name="board:mirror">
        <xsl:with-param name="sol" select="$solb"/>
      </xsl:call-template>
    </xsl:variable>

    <xsl:variable name="solc">
      <xsl:call-template name="board:rot90">
        <xsl:with-param name="sol" select="$solb"/>
      </xsl:call-template>
    </xsl:variable>

    <xsl:variable name="solC">
      <xsl:call-template name="board:mirror">
        <xsl:with-param name="sol" select="$solc"/>
      </xsl:call-template>
    </xsl:variable>

    <xsl:variable name="sold">
      <xsl:call-template name="board:rot90">
        <xsl:with-param name="sol" select="$solc"/>
      </xsl:call-template>
    </xsl:variable>

    <xsl:variable name="solD">
      <xsl:call-template name="board:mirror">
        <xsl:with-param name="sol" select="$sold"/>
      </xsl:call-template>
    </xsl:variable>

    <!-- collect all 8 possibilities -->
    <xsl:variable name="sols0">
      <sol><xsl:value-of select="$sola"/></sol>
      <sol><xsl:value-of select="$solA"/></sol>
      <sol><xsl:value-of select="$solb"/></sol>
      <sol><xsl:value-of select="$solB"/></sol>
      <sol><xsl:value-of select="$solc"/></sol>
      <sol><xsl:value-of select="$solC"/></sol>
      <sol><xsl:value-of select="$sold"/></sol>
      <sol><xsl:value-of select="$solD"/></sol>
    </xsl:variable>
    <xsl:variable name="sols" select="exslt:node-set($sols0)"/>

    <!-- sort them -->
    <xsl:variable name="sorted0">
      <xsl:for-each select="$sols/*">
        <xsl:sort data-type="number"/>
        <xsl:copy-of select="."/>
      </xsl:for-each>
    </xsl:variable>
    <xsl:variable name="sorted" select="exslt:node-set($sorted0)"/>

    <!-- return unique (sorted) solutions -->
    <xsl:variable name="unique0">
      <xsl:copy-of select="$sorted/*[not(.=preceding-sibling::*[1])]"/>
    </xsl:variable>
    <xsl:copy-of select="exslt:node-set($unique0)"/>
  </xsl:template>


  <!-- output row of (2-8) unique rotation and/or mirror solutions -->
  <xsl:template match="sol">
    <xsl:variable name="sorted0">
      <xsl:call-template name="board:sorted">
        <xsl:with-param name="sola" select="."/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="sorted" select="exslt:node-set($sorted0)"/>

<tr>
<td>
    <xsl:value-of select="position()"/>
</td>
    <xsl:for-each select="$sorted/*">
      <xsl:variable name="board0">
        <xsl:call-template name="chess:toboard0">
          <xsl:with-param name="sol" select="."/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="board" select="exslt:node-set($board0)"/>
<td>
      <xsl:call-template name="chess:draw">
        <xsl:with-param name="board" select="$board"/>
      </xsl:call-template>
</td>
    </xsl:for-each>
</tr>

<tr>
<td/>
<td>
<span style="font-size:80%">
    <xsl:value-of select="."/>
</span>
</td>
</tr>
  </xsl:template>
  


<!-- glue templates for converting a solution to board notation -->

  <!-- generate bq at col $q <f></f>...<f></f><f>bq</f><f></f>...<f></f> -->
  <xsl:template name="chess:row">
    <xsl:param name="n"/>
    <xsl:param name="q"/>

    <xsl:if test="$n > 0">
      <xsl:call-template name="chess:row">
        <xsl:with-param name="n" select="$n - 1"/>
        <xsl:with-param name="q" select="$q"/>
      </xsl:call-template>

      <f><xsl:if test="$n=$q">bq</xsl:if></f>
    </xsl:if>
  </xsl:template>

  <!-- recursively generate complete board ... from a given solution -->
  <xsl:template name="chess:toboard">
    <xsl:param name="sol"/>
    <xsl:param name="n" select="string-length($sol)"/>

    <xsl:if test="$sol">
      <xsl:call-template name="chess:toboard">
        <xsl:with-param name="sol" select="substring($sol, 2)"/>
        <xsl:with-param name="n" select="$n"/>
      </xsl:call-template>

      <row>
        <xsl:call-template name="chess:row">
          <xsl:with-param name="n" select="$n"/>
          <xsl:with-param name="q" select="substring($sol, 1, 1)"/>
        </xsl:call-template>
      </row>
    </xsl:if>
  </xsl:template>

  <!-- generate complete board with black queens from a given solution -->
  <xsl:template name="chess:toboard0">
    <xsl:param name="sol"/>

    <board>
      <xsl:call-template name="chess:toboard">
        <xsl:with-param name="sol" select="$sol"/>
      </xsl:call-template>
    </board>
  </xsl:template>



<!-- templates to "draw" a board, HTML output -->

  <!-- draw outer border -->
  <xsl:template name="chess:draw">
    <xsl:param name="board"/>

    <xsl:apply-templates select="$board/board"/>
  </xsl:template>

  <!-- draw board of fields without spacing -->
  <xsl:template match="board">
<table cellpadding="0" cellspacing="0">
    <xsl:apply-templates select="row"/>
</table>
  </xsl:template>

  <!-- row -->
  <xsl:template match="row">
<tr>
    <xsl:apply-templates select="f">
      <xsl:with-param name="row" select="position()"/>
    </xsl:apply-templates>
</tr>
  </xsl:template>

  <!-- field -->
  <xsl:template match="f">
    <xsl:param name="row"/>
<td>
    <xsl:call-template name="img">
      <xsl:with-param name="col" 
        select="substring('wb',1+((position()+$row) mod 2),1)"/>
    </xsl:call-template>
</td>
  </xsl:template>

  <!-- image based on figure name "." and field color $col -->
  <xsl:template name="img">
    <xsl:param name="col"/>

    <xsl:element name="img">
      <xsl:attribute name="style">display:block</xsl:attribute>
      <xsl:attribute name="src">
        <xsl:variable name="name" 
          select="concat(
                    'gif/',normalize-space(.),$col,'.gif'
                  )"
        />
        <xsl:value-of 
          select="concat(
                    substring($gifs/gif[@name=$name],1 div &internalImages;),
                    substring($name,1 div not(&internalImages;))
                  )"
        />
      </xsl:attribute>
    </xsl:element>
  </xsl:template>


<!-- data: scheme inline images -->
<xsl:variable name="gifs0">
<gif name="gif/bqw.gif">data:image/gif;base64,
R0lGODlhDAAMAMZBAAAAAAsLCw4ODhAQEBERERQUFBUVFRsbGx0dHR4eHiQkJDg4OD4+PkFBQUhI
SExMTFBQUFJSUlNTU1ZWVlhYWFlZWVxcXF1dXWZmZnBwcHR0dHx8fH5+fn9/f4WFhYeHh4+Pj5KS
kpaWlpeXl5+fn6CgoKKioqampqmpqaurq7KysrOzs7a2tre3t8XFxcvLy9XV1djY2N7e3uDg4OHh
4eLi4+Tk5Obm5+fn5+jo6Ovr6+3t7fj4+Pr6+vv7+/z8/P39/f//////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////yH+
EUNyZWF0ZWQgd2l0aCBHSU1QACwAAAAADAAMAAAHcYA3goOENzVBiCkzISYwLIg+QCE6EioiKCQY
NiM+PQ0uIS8lKy0nKhA+QS4fIRkbIBYnGjGpOAYeCQwOBSABPalBCgIAxAACC0HAFwgUzRADGcmI
NBUe1h4TO9JBPA/XHhE/2zIdBAEBBxw524jt7j6BADs=</gif>

<gif name="gif/bqb.gif">data:image/gif;base64,
R0lGODlhDAAMAKU+AAAAAAsLCw4ODg8PDxISEhMTExYWFhgYGBkZGR4eHh8fHycnJy8vLzg4ODo6
Oj09PUdHR0tLS0xMTFFRUVNTU1RUVFdXV1hYWFpaWl5eXmBgYGJiYmlpaWpqam9vb3BwcHFxcXR0
dHt7e319fYGBgYKCgoWFhYeHh4uLi5GRkZqampubm5ycnJ+fn6ioqKqqqqurq7CwsLKysrOzs7a2
tra2t7i4ubm5ubu7u7y8vL+/v8DAwMLCwsPDw////////yH+EUNyZWF0ZWQgd2l0aCBHSU1QACwA
AAAADAAMAAAGaUBbbUgs1no6HakF6tBGyV1PI4ucRqWR48XR9R4lUeoUUn1QilxvRRFNOhnJh8OS
3gIeQ4Mx2ARiUjoLAISEAgk6gRgHF40VBA6JSDEWJpYmFzOSOjgQlyYQN5swHggBAQUfLptJra48
QQA7</gif>

<gif name="gif/w.gif">data:image/gif;base64,
R0lGODlhDAAMAMIEAPLy8vPz8/T09Pj4+P///////////////yH+EUNyZWF0ZWQgd2l0aCBHSU1Q
ACwAAAAADAAMAAADGCi03KTOwcgmJeCyrHH3HacFn0UNaKqiCQA7</gif>

<gif name="gif/b.gif">data:image/gif;base64,
R0lGODlhDAAMAMIGALe3t729vr+/v8bGxsfHx8rKyv///////yH+EUNyZWF0ZWQgd2l0aCBHSU1Q
ACwAAAAADAAMAAADGQiy3KLOwcgmfbfmZWmPn7RhWzCcA6GuRQIAOw==</gif>
</xsl:variable>
<xsl:variable name="gifs" select="exslt:node-set($gifs0)"/>


<!--
     IE browser exslt:node-set() (XSLT 1.0+), w/o msxsl pollution above

     from http://dpcarlisle.blogspot.com/2007/05/exslt-node-set-function.html
-->
<msxsl:script xmlns:msxsl="urn:schemas-microsoft-com:xslt"
              language="JScript" implements-prefix="exslt"
>
  this['node-set'] = function (x) {
    return x;
  } 
</msxsl:script>

</xsl:stylesheet>
