Math

1. Test For Numeric Values?
2. A library of trig functions
3. Testing for set membership
4. Simple computations in XSLT
5. How to accumulate subtotals at each grouping level
6. Subtraction Doesnt work
7. Total
8. How to select Node with maximum attribute value
9. Hex arithmatic in XSLT
10. Quantity times price
11. Calculations in XSL using multiple XML data fileds
12. Max and Min
13. Finding element with min value in multiple files
14. Real Numbers, variations.
15. max(), min() and avg() functions
16. Random Number Generator
17. Decimal to Hexadecimal conversion
18. Calculate mantissa and exponent
19. Sum of products
20. Summation, numbers contain comma's (European format)
21. Any one know how to eliminate Trailing Zeros
22. Number precision
23. Minimum value
24. Sum function - portable
25. MathML to SVG
26. Binary OR function
27. fibonacci
28. union operator
29. Numbers to Words
30. Random selection
31. Bit Testing
32. XSLT for math computations
33. Why does 3 > 2 > 1 return false() and 1 < 2 < 3 return true()??
34. Sum over computed nodes
35. Count unique terms
36. Maths in xsl
37. mod 10 xpath vs math

1.

Test For Numeric Values?

Michael Kay

> I want to test a node value to see whether 
> it is numeric or not? (Decides HTML align formatting)

<xsl:if test="string(number(.))='NaN'">
     <xsl:attribute name="align">right</xsl:attribute>  
</xsl:if>

2.

A library of trig functions

Dimitre Novatchev

Sourceforge - a library of trig functions by our own Dimitre!

3.

Testing for set membership

Michael Kay


> I have a root node $r and some descendant $d. I have to 
> select the first (or last) node in between them ($d included) 
> that satisfies a certain criteria.

You are trying to test "is $d one of the nodes in descendant-or-self::*". Try writing this as

  count(.|descendant-or-self::*) != count(descendant-or-self::*)

or better, if you are prepared to use an extension function

  boolean(set:intersection(., descendant-or-self::*))

using the EXSLT extension library. (This is better because the pure XSLT solution navigates the descendant-or-self axis twice.)

Another approach to this problem is to work from both ends: find the intersection of the descendants of $r and the ancestors of $d, using:

set:intersection($r/descendant-or-self::*,
$d/ancestor-or-self::*)[criteria()]

If you don't want to use set:intersection, the standard XPath expression to get the intersection of P and Q is

P[count(.|Q) = count(Q)]

4.

Simple computations in XSLT

Mike Kay

Uing simple expressions such as
 
<xsl:variable name="sum" 
	select="$a + $b"/>
<xsl:variable name="difference" 
	select="$a - $b"/>
<xsl:variable name="product" 
	select="$a * $b"/>

            

5.

How to accumulate subtotals at each grouping level

David Carlisle


To convert

<doc>

<element name="a" value="1" />
<element name="a" value="2" />
<element name="a" value="3" />
<element name="a" value="2" />
<element name="b" value="1" />
<element name="b" value="1" />
<element name="b" value="3" />
<element name="b" value="4" />
<element name="a" value="2" />
<element name="a" value="3" />
<element name="a" value="6" />
<element name="a" value="2" />
<element name="c" value="1" />
<element name="c" value="4" />
<element name="c" value="2" />
<element name="c" value="1" />

</doc>

by grabbing consecutive runs of the same @name 
into a containing section element, and 
adding a total for each section, to produce:

<section>
<element name="a" value="1"/>
<element name="a" value="2"/>
<element name="a" value="3"/>
<element name="a" value="2"/>
<total>8</total>
</section>
<section>
<element name="b" value="1"/>
<element name="b" value="1"/>
<element name="b" value="3"/>
<element name="b" value="4"/>
<total>9</total>
</section>
<section>
<element name="a" value="2"/>
<element name="a" value="3"/>
<element name="a" value="6"/>
<element name="a" value="2"/>
<total>13</total>
</section>
<section>
<element name="c" value="1"/>
<element name="c" value="4"/>
<element name="c" value="2"/>
<element name="c" value="1"/>
<total>8</total>
</section>

You could do


<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Tranform"     
	version="1.0">



<xsl:template match="doc">
  <xsl:apply-templates mode="sec" 
	select="*[1]"/>
</xsl:template>


<xsl:template mode="sec" match="*">
<xsl:variable name="n" select="@name"/>
<section>
<xsl:apply-templates mode="elem" 
	select="."/>
</section>
<xsl:apply-templates mode="sec" 
        select="following-sibling::*[not(@name=$n)][1]"/>
</xsl:template>


<xsl:template mode="elem" match="*">
<xsl:variable name="n" select="@name"/>
<xsl:param name="t" select="0"/>
<xsl:copy-of select="."/>
<xsl:apply-templates 
   mode="elem"
  select="following-sibling::*[position()=1 
	and (@name=$n)]">
  <xsl:with-param name="t" 
	select="$t+@value"/>
</xsl:apply-templates>
<xsl:if test=
         "not(following-sibling::*[position()=1 
	and (@name=$n)])">
<total>
<xsl:value-of select="$t+@value"/>
</total>
</xsl:if>
</xsl:template>


</xsl:stylesheet>


            

6.

Subtraction Doesnt work

Christopher R. Maden


>For some reason, the first two variable declarations work fine, but the
>third one gives this error:
>pattern = '$pm-$hourMinutes' Extra illegal tokens: '$', 'hourMinutes'
>
>      <xsl:variable name="hours" select="round($pm div 60)"/>
>      <xsl:variable name="hourMinutes" select="$hours*60"/>
>      <xsl:variable name="minutes" select="$pm-$hourMinutes"/>
>
>Can't I subtract in XSL? 
    

The problem is that a variable name is an XML QName, which can include hyphens. Without any spaces, the select value is parsed as '$' 'pm-' '$' 'hourMinutes' - in other words, a variable indicator, the name of the variable 'pm-', and then another variable indicator, which isn't legal there. Instead, try

<xsl:variable name="minutes" select="$pm - $hourMinutes"/>

7.

Total

Mike Brown

XML output is the default. Elements (within templates) that are not associated with the XSL namespace (i.e., they don't have the xsl: prefix) are going to be in your output.

If the field is numeric, use the sum() function -- variables aren't necessary. The argument to the function is a node-set containing the numbers to be totaled. Indicate the node-set by using an XPath expression that identifies the appropriate nodes in the original XML. Here is an example where this expression is very explicit: '/doc/num' matches element nodes named 'num' that are children of elements named 'doc' that are children of the root node.

Given this XML:

<?xml version="1.0"?>
<doc>
    <num>1</num>
    <num>7</num>
    <notnum>hello world</notnum>
    <num>4</num>
</doc>

Here is the simplest XSL that gives you the total of all
'num' element children of 'doc':

<?xml version="1.0"?>
<xsl:stylesheet 
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 version="1.0">
    <xsl:template match="/">
         <xsl:element name="grandTotal">
            <xsl:value-of select="sum(/doc/num)"/>
         </xsl:element>
       
    </xsl:template>
</xsl:stylesheet>






...This is exactly the same as:

<?xml version="1.0"?>
<xsl:stylesheet 
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 version="1.0">

    <xsl:template match="/">
        <grandTotal><xsl:value-of 
	select="sum(/doc/num)"/></grandTotal>
    </xsl:template>
</xsl:stylesheet>

...And if you really need to use variables:

<?xml version="1.0"?>
<xsl:stylesheet 
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 version="1.0">
    <xsl:template match="/">
        <xsl:variable 
	name="total" select="sum(/doc/num)"/>
        <grandTotal><xsl:value-of 
	      select="$total"/></grandTotal>
    </xsl:template>
</xsl:stylesheet>

            

8.

How to select Node with maximum attribute value

Mike Kay



<xsl:template name="get-max">
  <xsl:param name="nodes"/>
    <xsl:choose>
      <xsl:when test="$nodes">
	<xsl:variable name="max-of-rest">
	  <xsl:call-template name="get-max">
	    <xsl:with-param name="nodes" 
                select="$nodes[position()!=1]"/>
	  </xsl:call-template>
	</xsl:variable>
	<xsl:choose>
	  <xsl:when test="nodes[1]/@mid > $max-of-rest">
	    <xsl:value-of select="nodes[1]/@mid"/>
	  </xsl:when>
	  <xsl:otherwise>
	    <xsl:value-of select="$max-of-rest"/>
	  </xsl:otherwise>
	</xsl:choose>
      </xsl:when>
      <xsl:otherwise>
	<xsl:value-of 
              select="-1 div 0"/> <!-- minus infinity -->
	</xsl:otherwise>
      </xsl:choose>
    </xsl:template>

Later note:


> I want to use the variable in an XPath 
> outside of the xsl:for-each, ie in
> another scope.
David Carlisle fills in,

<xsl:variable name="x">
either my solution or Mike Kay's
</xsl:variable>

then you can use $x and it'll coerce from a RTF to a string
to a number if need be.



9.

Hex arithmatic in XSLT

Nate Austin

This template adds hex values F0F0F0F0 and 0F0F0F0F to come up with FFFFFFFF as it should be. This was tested with Xalan 1.0.1.

<?xml version="1.0"?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
                             
  <xsl:output method="text"/>
  
  <xsl:template match="/">
    <xsl:call-template name="hexMath">
      <xsl:with-param name="op1" select="'F0F0F0F0'"/>
      <xsl:with-param name="oper" select="'+'"/>
      <xsl:with-param name="op2" select="'0F0F0F0F'"/>
    </xsl:call-template>
  </xsl:template>
  
  <xsl:template name="hexMath">
    <xsl:param name="op1"/>
    <xsl:param name="oper"/>
    <xsl:param name="op2"/>
    <xsl:variable name="numOp1">
      <xsl:call-template name="hexToDec">
        <xsl:with-param name="hexVal" select="$op1"/>
        <xsl:with-param name="decVal" select="0"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="numOp2">
      <xsl:call-template name="hexToDec">
        <xsl:with-param name="hexVal" select="$op2"/>
        <xsl:with-param name="decVal" select="0"/>
      </xsl:call-template>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="$oper = '+'">
        <xsl:variable name="res">
          <xsl:call-template name="decToHex">
            <xsl:with-param name="decVal" select="$numOp1 + $numOp2"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="$res"/>
      </xsl:when>
      <xsl:when test="$oper = '-'">
        <xsl:variable name="res">
          <xsl:call-template name="decToHex">
            <xsl:with-param name="decVal" select="$numOp1 - $numOp2"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="$res"/>
      </xsl:when>
      <xsl:when test="$oper = '*'">
        <xsl:variable name="res">
          <xsl:call-template name="decToHex">
            <xsl:with-param name="decVal" select="$numOp1 * $numOp2"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="$res"/>
      </xsl:when>
      <xsl:when test="$oper = 'div'">
        <xsl:variable name="res">
          <xsl:call-template name="decToHex">
            <xsl:with-param name="decVal" select="$numOp1 div $numOp2"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="$res"/>
      </xsl:when>
      <xsl:when test="$oper = 'mod'">
        <xsl:variable name="res">
          <xsl:call-template name="decToHex">
            <xsl:with-param name="decVal" select="$numOp1 mod $numOp2"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="$res"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message>Error!  Unknown operation!</xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template name="singleDecToHex">
    <xsl:param name="num"/>
    <xsl:choose>
      <xsl:when test="$num &lt; 16 and $num &gt;= 0">
        <xsl:variable name="table" select="'0123456789ABCDEF'"/>
        <xsl:value-of select="substring($table,$num + 1,1)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message>
Number to convert to hexadecimal out of range</xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template name="singleHexToDec">
    <xsl:param name="hex"/>
    <xsl:variable name="table" select="'0123456789ABCDEF'"/>
    <xsl:value-of select="string-length(substring-before($table,$hex))"/>
  </xsl:template>
  
  <xsl:template name="findPower">
    <xsl:param name="value"/>
    <xsl:param name="currPower"/>
    <xsl:param name="multiplier"/>
    <xsl:param name="accumulator"/>
    <xsl:choose>
      <xsl:when test="$value &lt; $accumulator">
        <xsl:value-of select="$accumulator div $multiplier"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="findPower">
          <xsl:with-param name="value" select="$value"/>
          <xsl:with-param name="currPower" select="$currPower + 1"/>
          <xsl:with-param name="multiplier" select="$multiplier"/>
          <xsl:with-param name="accumulator" 
     select="$accumulator * $multiplier"/>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template name="decToHex">
    <xsl:param name="decVal"/>
    <xsl:variable name="power">
      <xsl:call-template name="findPower">
        <xsl:with-param name="value" select="$decVal"/>
        <xsl:with-param name="currPower" select="0"/>
        <xsl:with-param name="multiplier" select="16"/>
       <xsl:with-param name="accumulator" select="1"/> 
     <!-- everything to 0 power is 1 -->
      </xsl:call-template>
    </xsl:variable>
    <xsl:call-template name="decToHexIter">
      <xsl:with-param name="decVal" select="$decVal"/>
      <xsl:with-param name="hexVal" select="''"/>
      <xsl:with-param name="power" select="$power"/>
    </xsl:call-template>
  </xsl:template>
  
  <xsl:template name="decToHexIter">
    <xsl:param name="decVal"/>
    <xsl:param name="hexVal"/>
    <xsl:param name="power"/>
    <xsl:choose>
      <xsl:when test="$decVal &gt; 0">
        <xsl:variable name="currDecVal" 
     select="($decVal - ($decVal mod $power)) div $power"/>
        <xsl:variable name="hexDigit">
          <xsl:call-template name="singleDecToHex">
            <xsl:with-param name="num" select="$currDecVal"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:call-template name="decToHexIter">
          <xsl:with-param name="decVal" 
 select="$decVal - ($currDecVal * $power)"/>
          <xsl:with-param name="hexVal" select="concat($hexVal,$hexDigit)"/>
          <xsl:with-param name="power" select="$power div 16"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$hexVal"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template name="raiseToPower">
    <xsl:param name="number"/>
    <xsl:param name="power"/>
    <xsl:call-template name="raiseToPowerIter">
      <xsl:with-param name="multiplier" select="$number"/>
      <xsl:with-param name="accumulator" select="1"/>
      <xsl:with-param name="reps" select="$power"/>
    </xsl:call-template>
  </xsl:template>
  
  <xsl:template name="raiseToPowerIter">
    <xsl:param name="multiplier"/>
    <xsl:param name="accumulator"/>
    <xsl:param name="reps"/>
    <xsl:choose>
      <xsl:when test="$reps &gt; 0">
        <xsl:call-template name="raiseToPowerIter">
          <xsl:with-param name="multiplier" select="$multiplier"/>
          <xsl:with-param name="accumulator" 
     select="$accumulator * $multiplier"/>
          <xsl:with-param name="reps" select="$reps - 1"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$accumulator"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template name="hexToDec">
    <xsl:param name="hexVal"/>
    <xsl:param name="decVal"/>
    <xsl:variable name="hexLength" select="string-length($hexVal)"/>
    <xsl:choose>
      <xsl:when test="$hexLength &gt; 0">
        <xsl:variable name="hexPos">
          <xsl:call-template name="singleHexToDec">
            <xsl:with-param name="hex" select="substring($hexVal,1,1)"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="addToDec">
          <xsl:call-template name="raiseToPower">
            <xsl:with-param name="number" select="16"/>
            <xsl:with-param name="power" select="$hexLength - 1"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:call-template name="hexToDec">
          <xsl:with-param name="hexVal" select="substring($hexVal,2)"/>
          <xsl:with-param name="decVal" 
     select="$decVal + ($addToDec * $hexPos)"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$decVal"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
</xsl:transform>

10.

Quantity times price

Nick Browne


>  But I need the sum of each (price*qty).  I tried everything I could think
> of, but no luck.  Any clues on how to do this?
    

Otherwise:

   <xsl:variable name="Items" select="//Item"/>

   <xsl:variable name="TotalValue">
    <xsl:call-template name="Total">
     <xsl:with-param name="Items" select="$Items"/>
     <xsl:with-param name="RunningTotal"    select="0"/>
    </xsl:call-template>
  </xsl:variable>

      Total   = <xsl:value-of 
     select="format-number($TotalValue, '####0.0#')"/>

 </xsl:template>

 <xsl:template name="Total">
  <xsl:param name="Items"/>
  <xsl:param name="RunningTotal"/>
  <xsl:choose>
   <xsl:when test="not($Items)">
    <xsl:copy-of select="$RunningTotal"/>
   </xsl:when>
   <xsl:otherwise>
  <xsl:variable name="CurrentTotal"
         select="$RunningTotal + ($Items[1]/@qty * $Items[1]/@price)"/>

   <xsl:call-template name="Total">
    <xsl:with-param name="Items" select="$Items[position()>1]"/>
    <xsl:with-param name="RunningTotal" select="$CurrentTotal"/>
   </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

11.

Calculations in XSL using multiple XML data fileds

Jeni Tennison



>I want the XSL to perform the following calculation
>
>	(q1/quotes/quote/last) - ((q2/quotes/quote/last)/7.45)
>
>I am hoping that this can be done in XSL but cannot find out how this can be
>done.
	

The only thing you're lacking is knowing how to divide in XSLT (well, actually in XPath). The '/' can't be used because it's used in XPath to separate steps, so instead 'div' is used instead:

  q1/quotes/quote/last - (q2/quotes/quote/last div 7.45)

To give the result to two decimal places, you need the format-number() XSLT function:

  format-number(q1/quotes/quote/last - (q2/quotes/quote/last div 7.45),
                '#,##0.00')

How the XML is generated won't matter in terms of how you solve this problem, but what XSLT processor you're using will: if you're using MSXML, make sure it's the latest version.

>If it cannot be done in XSL, can anyone suggest how else (script?) this can
>be done

If you're using MSXML, then you can use an extension function called msxsl:script to write functions in JScript or VBScript that can then be used within XPath as XPath functions. XSLT is enough to handle simple calculations, but if you get anything complicated (e.g. sin/cos) then you need something more, which msxsl:script provides. The details are given in the MSXML SDK.

12.

Max and Min

Jeni Tennison


> I would like to know if we can find out Maximum and Minumum of a element in
> a xml file using xsl or any other XML technologes.
	

The best way involves an XPath: the minimum value is the value of the node such that there are no other nodes that have a value less than that value; the maximum is the value of the node such that there are no other nodes with a value *more* than that value.

So, to find the maximum of the 'in' elements, use:

  in[not(parent::TIME/in &gt; .)]

[in English: "the 'in' element(s) for which it is *not* the case that there is a 'in' element (child of the 'in' element's parent, 'TIME') that has a value less than the value of this one"]

and to find the minimum:

  out[not(parent::TIME/out &lt; .)]

For some reason, I've also done this by sorting the elements, in either ascending or descending order, and picking out the first in the list. If you sort in ascending order, you'll get the minimum; if in descending order, the maximum.

So, to find the maximum of the 'in' elements, use:

  <xsl:for-each select="in">
    <xsl:sort select="." data-type="number" order="descending" />
    <xsl:if test="position() = 1">
      <xsl:value-of select="." />
    </xsl:if>
  </xsl:for-each>

Mike Kay adds:

But I'd express caution, certainly for large node-sets. This is likely to be an O(n-squared) solution (it certainly is in Saxon). Doing an XSLT sort and extracting the first or last element is likely to be O(n*log(n)). Doing a recursive walk of the node-set as described in XSLT Prog Ref page 171 is likely to be O(n).

13.

Finding element with min value in multiple files

Jeni Tennison


> How do I find max or min value of a given element in multiple files?
	

Lots of ways. A quick and simple way to find the minimum would be to have a variable hold the values:

<xsl:variable name="costs"
              select="document('Replies/File')/Document/Cost" />

And then to find those nodes in the $costs node set where there's no other node in the node set with a smaller value:

$costs[not($costs &lt; .)]

Another way is to use a sort in ascending order and then pick the first in the list:

<xsl:for-each select="$costs">
   <xsl:sort select="." data-type="number" />
   <xsl:if test="position() = 1">
      <xsl:value-of select="." />
   </xsl:if>
</xsl:for-each>

A final way involves constructing a new result tree fragment holding the costs:

<xsl:variable name="costs">
   <xsl:copy-of select="document(Replies/File)/Document/Cost" />
</xsl:variable>

and then to use this as the basis of a recursive solution. Apply templates to the first Cost in this list:

   <xsl:apply-templates select="$costs/Cost[1]" />

and have a template:

<xsl:template match="Cost" mode="minimum">
   <xsl:variable name="next"
                 select="following-sibling::Cost[. &lt; current()]" />
   <xsl:choose>
      <xsl:when test="$next">
         <xsl:apply-templates select="$next" mode="minimum" />
      </xsl:when>
      <xsl:otherwise>
         <xsl:value-of select="." />
      </xsl:otherwise>
   </xsl:choose>
</xsl:template>

This solution works best under XSLT 1.1, but you can use it now if you use the node-set() extension function provided under most XSLT processors. For now you need to apply templates with something like:

   <xsl:apply-templates select="saxon:node-set($costs)/Cost[1]" />

Which one of the above works best will depend on how many Costs you have, what your processor optimises and so on - try them to see.

14.

Real Numbers, variations.

Jarno Elovirta

XPath number with 20 digits gives you a head ache

 <xsl:value-of select="number(string('92125374252539897737'))" />
Results from XSLT processors I have on my machine:

  Output                XSLT Processor
  ======================================
  92125374252539904000  SAXON 6.1
                        Xalan-C 1.0
                        UXT 1.03.00
                        XT
  92125374252539900000  Xalan-J 2.0.D07
                        Xalan-J 1.2.2
                        MSXML 3.0
  9.21253742525399E19   Oracle V2 (beta)
  2147483647            iXSLT 2.0c

Interesting. Which, if any, is actually right?

Mike Kay comments:

Oracle's output (stated version) is definitely wrong: the result should never be in scientific notation.

15.

max(), min() and avg() functions

Steve Muench


> i want to get the maximum, minimum and average values of 
> xml elements....i
> want to use XSLTs for this purpose...does XSLT 
> support mathematical functions

	

XSLT supports count() and sum() aggegate functions.

By using a trick to assign a variable to a computed value, you can use XSLT to calculate min, max, and avg like this:

Let's say your source document is:

  <list>
    <item>15</item>
    <item>10</item>
    <item>20</item>
  </list>

Then it's easy to assign a value to the sum() of the items by doing:

   <xsl:variable name="the_sum" value="sum(/list/item)"/>

or the count of the items:

   <xsl:variable name="the_count" value="count(/list/item)"/>

for the average, you can do something like this:

   <xsl:variable name="the_avg" 
     select="sum(/list/item) div count(/list/item)"/>

for the max, you can do:

   <!--
    | assign variable based on picked the first item in
    | the numerically-sorted-descending list of items.
    +>
   <xsl:variable name="the_max">
     <xsl:for-each select="/list/item">
       <xsl:sort data-type="number" order="descending"/>
       <xsl:if test="position()=1">
	  <xsl:value-of select="."/></xsl:if>
     </xsl:for-each>
   </xsl:variable>

for the min, you just reverse the sort order:

   <!--
    | assign variable based on picked the first item in
    | the numerically-sorted-descending list of items.
    +>
   <xsl:variable name="the_min">
     <xsl:for-each select="/list/item">
       <xsl:sort data-type="number" order="ascending"/>
       <xsl:if test="position()=1">
	  <xsl:value-of select="."/></xsl:if>
     </xsl:for-each>
   </xsl:variable>

16.

Random Number Generator

Michael Kay

If you're using a Java processor, something like

<xsl:variable name="rnd" select="math:random()"
xmlns:math="java:java.lang.Math"/>

Details vary slightly depending on the processor.

17.

Decimal to Hexadecimal conversion

Edmund Mitchell

I think Goetz posted this a long time ago ( Sorry if the cite is off)

<xsl:template name="printHex">
  <xsl:param name="number">0</xsl:param>

  <xsl:variable name="low">
    <xsl:value-of select="$number mod 16"/>
  </xsl:variable>

  <xsl:variable name="high">
    <xsl:value-of select="floor($number div 16)"/>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="$high &gt; 0">
      <xsl:call-template name="printHex">
        <xsl:with-param name="number">
          <xsl:value-of select="$high"/>
        </xsl:with-param>
      </xsl:call-template>  
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>0x</xsl:text>
    </xsl:otherwise>
  </xsl:choose>  

  <xsl:choose>
    <xsl:when test="$low &lt; 10">
      <xsl:value-of select="$low"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="temp">
        <xsl:value-of select="$low - 10"/>
      </xsl:variable>

      <xsl:value-of select="translate($temp, '012345', 'ABCDEF')"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>  

18.

Calculate mantissa and exponent

Mitchell, Edmund

here it is, courtesy of sdussinger:

sample.xml:

 <a>
   <num>0.00000004</num>
   <num>123456</num>
   <num>-756444</num>
 </a>
 

sample.xsl:

 <xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
       version="1.0">
     <xsl:template match="/">
       <xsl:for-each select='/a/num'>
  <xsl:call-template name="normalize-number">
  <xsl:with-param name="num" select="."/>
            </xsl:call-template>
             <xsl:text>&#10;</xsl:text>
       </xsl:for-each>
     </xsl:template>
 
 <!--
   Generic template to normalize a number
   Pass a param called "num" containing the number you wish
   to normalize
 -->
 <xsl:template name="normalize-number">
  <xsl:param name="num" select="0"/>
 
  <xsl:call-template name="do-normalize">
    <xsl:with-param name="mantissa" select="$num"/>
    <xsl:with-param name="exp" select="0"/>
  </xsl:call-template>
 
 </xsl:template>
 
 <!--
   Simple template for formatting the exponent. This
   is needed to ensure that the '+' sign shows up for
   positive exponents.
 -->
 <xsl:template name="format-exp">
  <xsl:param name="exp" select="0"/>
 
  <xsl:if test="$exp &gt;= 0">
    <xsl:text>+</xsl:text>
  </xsl:if>
  <xsl:value-of select="$exp"/>
 
 </xsl:template>
 
 <!--
   This is the body of the normalizer. It recursively
   processes the number until it's in the correct form
 -->
 <xsl:template name="do-normalize">
 
  <xsl:param name="mantissa" select="0"/>
  <xsl:param name="exp" select="0"/>
 
  <xsl:choose>
 
    <!-- If the mantissa is 0, we're done. Just print it out -->
    <xsl:when test="$mantissa = 0.0 or $mantissa = -0.0">
      <xsl:value-of select="format-number(0.0, '#,##0.00000')"/>
      <xsl:text>e</xsl:text>
      <xsl:call-template name="format-exp">
        <xsl:with-param name="exp" select="$exp"/>
      </xsl:call-template>
    </xsl:when>
 
    <!-- If we've gotten the number normalized, then print it out.
         This is for a positive number -->
    <xsl:when test="$mantissa &gt;= 1.0 and $mantissa &lt; 10.0">
      <xsl:value-of select="format-number (number($mantissa),
 '#,##0.00000')"/>
      <xsl:text>e</xsl:text>
      <xsl:call-template name="format-exp">
        <xsl:with-param name="exp" select="$exp"/>
      </xsl:call-template>
    </xsl:when>
 
    <!-- We've gotten a negative number normalized, print it -->
    <xsl:when test="$mantissa &gt; -10.0 and $mantissa &lt;= -1.0">
      <xsl:value-of select="format-number (number($mantissa),
 '#,##0.00000')"/>
      <xsl:text>e</xsl:text>
      <xsl:call-template name="format-exp">
        <xsl:with-param name="exp" select="$exp"/>
      </xsl:call-template>
    </xsl:when>
 
    <!-- In this case we've got a number greater than abs(10.0). We
         add one to the exponent, divide the mantissa by 10, 
 and recurse -->
    <xsl:when test="$mantissa &gt;= 10.0 or $mantissa &lt;= -10.0">
      <xsl:call-template name="do-normalize">
        <xsl:with-param name="mantissa" select="$mantissa div 10"/>
        <xsl:with-param name="exp" select="$exp + 1"/>
      </xsl:call-template>
    </xsl:when>
 
    <!-- In this case we have a number whose abs() < 1.0. We subtract 1
         from the exponent, multiply the mantissa by 10 and 
 recurse. -->
    <xsl:when test="$mantissa &gt; -1.0 and $mantissa &lt; 1.0">
      <xsl:call-template name="do-normalize">
        <xsl:with-param name="mantissa" select="$mantissa * 10"/>
        <xsl:with-param name="exp" select="$exp - 1"/>
      </xsl:call-template>
    </xsl:when>
  </xsl:choose>
 
 </xsl:template>
 </xsl:stylesheet>
 

output:

 [ejm@localhost xslt]$ java  com.icl.saxon.StyleSheet 
 sample.xml sample.xsl
 <?xml version="1.0" encoding="utf-8"?>
 4.00000e-8
 1.23456e+5
 -7.56444e+5

19.

Sum of products

Marco

You could use this recursive template:

 <xsl:template name="prod-sum">
   <xsl:param name="sum">0</xsl:param>
   <xsl:param name="nodes" />
   <xsl:choose>
     <xsl:when test="$nodes">
       <xsl:call-template name="prod-sum">
         <xsl:with-param name="sum" 
           select="$sum + number($nodes[1]/a) * number($nodes[1]/b)" />
         <xsl:with-param name="nodes" select="$nodes[position()!=1]" />
       </xsl:call-template>
     </xsl:when>
     <xsl:otherwise>
       <xsl:value-of select="$sum" />
     </xsl:otherwise>
   </xsl:choose>
 </xsl:template>

20.

Summation, numbers contain comma's (European format)

Mike Kay


  > How do I use the sum function on xml such as:
  >
  > &lt;Amount>12,345.12&lt;/Amount>
  > &lt;Amount>132,345.12&lt;/Amount>
  > &lt;Amount>2,345.12&lt;/Amount>
  >
  > If I use Total Price = &lt;xsl:value-of select="sum(//Amount)"/>
  > I get the
  > result NaN. However, if the data does not contain ',' the sum works
  > correctly.

As a general principle, it's best to hold data in XML documents in a form that's optimised for processing rather than for display to humans. Using schemas will encourage that further. So I would say, first do a transformation to a document that holds these numbers in their canonical form (12345.12 etc), which you can achieve using translate(., ',', ''), and then apply the summation to the result.

Jeni T then expands on that (as she is wont to do :-)

This is because XPath can't convert '2,345.12' to a number (due to the comma, as you found). The best solution is to change your XML source document so that it doesn't contain commas in numeric values. This is the best solution because (a) it makes your XSLT simple (you can use sum()) and (b) according to XML Schema and therefore the rest of the XML world, the text representation of numbers doesn't contain commas; removing them will mean that you can use XML Schema or other languages that use the XML Schema data types with your XML as well.

If you can't do that, then you need a recursive template that will enable you to do the conversion of the string value of the Amount elements to the numbers that you want them to be. The conversion is really simple - get rid of the commas with the translate() function. With $amount being an Amount element:

    translate($amount, ',', '')

You could use one of Dimitre's generic templates, or the following (tail-)recursive template, which works through the Amount elements one by one, keeping track of the subtotal as it goes, and finally returns the total when it runs out of Amount elements:

  &lt;xsl:template name="sumAmounts">
    &lt;xsl:param name="amounts" select="Amount" />
    &lt;xsl:param name="subtotal" select="0" />
    &lt;xsl:choose>
      &lt;xsl:when test="$amounts">
        &lt;xsl:call-template name="sumAmounts">
          &lt;xsl:with-param name="amounts"
                          select="$amounts[position() > 1]" />
          &lt;xsl:with-param name="subtotal"
            select="$subtotal + translate($amounts[1], ',', '')" />
        &lt;/xsl:call-template>
      &lt;/xsl:when>
      &lt;xsl:otherwise>
        &lt;xsl:value-of select="$subtotal" />
      &lt;/xsl:otherwise>
    &lt;/xsl:choose>
  &lt;/xsl:template>
  
  

21.

Any one know how to eliminate Trailing Zeros

Joerg Heinicke



 > Any one know how to eliminate Trailing Zeros.
 > 
 > Decimal-Format    from  13.70  to be   13.7  ??
 

 <xsl:value-of select="format-number('13.70','#,##0.#')"/>

result: 13.7

If you want the zeros back:

 <xsl:value-of select="format-number('13.70','#,##0.00')"/>

22.

Number precision

Jeni Tennison



> Is there any standard default rule governing the conversion of a
> double to a string when the double is of the form "0.x1" (where x is
> any number of 0s). i.e. should string(0.000001) return "0.000001",
> "1e-6", "1e-06", "1e-006", etc, or does the spec leave this open as
> implementation specific? If there is a specific rule hidden
> somewhere, where is the cut-off point for the number of 0s to output
> before the format changes to "1e-x"?

The data type "double" doesn't technically exist in XPath 1.0 -- there are only strings, numbers, booleans and node sets. However, numbers in XPath 1.0 are double-precision 64-bit format IEEE 754 values.

Numbers are converted to strings using the following rules:

* NaN is converted to the string NaN
* positive zero is converted to the string 0
* negative zero is converted to the string 0
* positive infinity is converted to the string Infinity
* negative infinity is converted to the string -Infinity
* if the number is an integer, the number is represented in decimal form as a Number with no decimal point and no leading zeros, preceded by a minus sign (-) if the number is negative
* otherwise, the number is represented in decimal form as a Number including a decimal point with at least one digit before the decimal point and at least one digit after the decimal point, preceded by a minus sign (-) if the number is negative; there must be no leading zeros before the decimal point apart possibly from the one required digit immediately before the decimal point; beyond the one required digit after the decimal point there must be as many, but only as many, more digits as are needed to uniquely distinguish the number from all other IEEE 754 numeric values.

Source

As you can see, there's nothing about using scientific notation for the string value of a number in this description. XPath/XSLT 1.0 processors shouldn't use the syntax "1e-6", "1e-06", "1e-006", etc, but should use "0.000001". This means that aside from NaN, Infinity and -Infinity, the string value of a number can be converted back into a number using the number() function.

You can change the format a little using the format-number() function top get a different string, but format-number() doesn't provide scientific notation either.

A proper double value type will be introduced in XPath 2.0 (xs:double). Currently, it looks likely that you'll be able to create doubles with literals that use scientific notation:

[134] DoubleLiteral ::= (("." [0-9]+) |
                         ([0-9]+ ("." [0-9]*)?))
                        ([e] | [E]) ([+] | [-])? [0-9]+

source

When you convert xs:double values to a string, they'll use the canonical lexical representation for xs:double as defined in the XML Schema: Datatypes Recommendation:

The canonical representation for double is defined by prohibiting certain options from the Lexical representation (para 3.2.5.1). Specifically, the exponent must be indicated by "E". Leading zeroes and the preceding optional "+" sign are prohibited in the exponent. For the mantissa, the preceding optional "+" sign is prohibited and the decimal point is required. For the exponent, the preceding optional "+" sign is prohibited. Leading and trailing zeroes are prohibited subject to the following: number representations must be normalized such that there is a single digit to the left of the decimal point and at least a single digit to the right of the decimal point.

Source

So the double 0.000001 should be turned into the string "1.0E-6" as far as I can tell.

According to the XSLT 2.0 WD, the XSL WG will be adding support for scientific notation to the format-number() function as well (see http://www.w3.org/TR/xslt20/#issue-scientific-notation) which should give you more control over the format.

23.

Minimum value

Michael Kay

There are basically three approaches to finding a minumum value:

(1) use a recursive template to visit each of the nodes, keeping track of the minimum value found so far
(2) sort the nodes and select the first/last one in the sorted set
(3) use an XPath expression to find the node whose value is less than every other value (someone posted this solution for you).

The interesting thing is that they have very different scaleability. (1) visits each node once so it has O(n) performance; (2) does a sort so it has O(n log n) performance, (3) if implemented crudely will compare every node with every other, so it potentially has O(n*n) performance (but processors may be able to optimize it). (However, although (1) performs in linear time, it may use a lot of memory depending on how well recursion is implemented).

Of course if you use Dimitre's FXSL library or the EXSLT max() and min() extension functions then you don't have to decide between these algorithms, someone else has already done the hard work for you.

In XPath 2.0 there are max() and min() functions in the core function library.

24.

Sum function - portable

Mike Brown


> is there any way that I can use sum() functions in XSLT on a comma separated
> list (received as an output from a template)?

It would be easiest if when you built the comma-separated list, you created a result tree fragment in which each value you want in the total is in a separate element. The string-value of the fragment can still give you the list output that you want, but you can also use XPath/XSLT's sum function on the node-set equivalent of the fragment. xalan:nodeSet() (or exsl:node-set() if you're using a more recent snapshot of Xalan) will convert the RTF to a node-set.

The most portable alternative is to use a recursive template to dissect the string.

Given XML:

<?xml version="1.0"?>
<some>
  <data>1</data>
  <data>2</data>
  <data>3</data>
  <data>5</data>
  <data>7</data>
  <data>11</data>
</some>

Here's a stylesheet that demonstrates both solutions:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:exsl="http://exslt.org/common"
                xmlns:xalan="http://xml.apache.org/xalan"
                exclude-result-prefixes="xalan exsl">
      
<xsl:output method="text"/>

<xsl:template match="/">
  <xsl:variable name="list">
    <xsl:for-each select="some/data">
      <dummyElement>
        <xsl:value-of select="."/>
      </dummyElement>
      <xsl:if test="position() != last()">
        <comma>,</comma>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>
  <xsl:text>The list is: </xsl:text>
  <xsl:value-of select="$list"/>
  <xsl:text>&#10;The sum is: </xsl:text>
  <xsl:choose>
    <xsl:when test="function-available('exsl:node-set')">
      <xsl:value-of select="count(exsl:node-set($list)/dummyElement)"/>
    </xsl:when>
    <xsl:when test="function-available('xalan:nodeSet')">
      <xsl:value-of select="sum(xalan:nodeSet($list)/dummyElement)"/> 
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="sum">
        <xsl:with-param name="str" select="string($list)"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="sum">
  <xsl:param name="str"/>
  <xsl:param name="total" select="0"/>
  <xsl:choose>
    <xsl:when test="contains($str,',')">
      <xsl:call-template name="sum">
        <xsl:with-param name="str" select="substring-after($str,',')"/>
        <xsl:with-param name="total" select="$total +
substring-before($str,',')"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$total + $str"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

25.

MathML to SVG

Max Froumentin



> Any Mathml to SVG stylesheets floating around?

"MathML to SVG Converter" by Schemasoft at schemasoft.com written in XSLT1.1, apparently. Crazy, huh?

Also, Marc Gaetano and Stephane Lavirotte from Universite de Nice have presented another converter (written in Java) at the MathML conference. See matmlconference.org and mainline.essi.fr (in French)

26.

Binary OR function

Various


> I couldn't find a "binary-or" (or "mask-or") operation in
> XPath 1.1 so I tried to emulate it with a template. I wrote a 
> recursive implementation but I'm not very happy with the result. I 
> wonder if there is a simpler (and maybe more efficient) solution. 
> Any suggestions?

Dion Houston proffers

I haven't had a chance to test it exhaustively, but it appears to work:

<xslt:template name="BinaryOr">
	<xslt:param name="op1" select="'0'"/>
	<xslt:param name="op2" select="'0'"/>
		
	<xslt:if test="$op1 != '' or $op2 != ''">
		<xslt:call-template name="BinaryOr">
			<xslt:with-param name="op1" 
	  select="substring($op1,
	  1, string-length($op1)-1)"/>
	   <xslt:with-param name="op2" 
	  select="substring($op2,1, string-length($op2)-1)"/>
		</xslt:call-template>
		<xslt:choose>
			<xslt:when test="$op2 mod 2">1</xslt:when>
			<xslt:otherwise><xslt:value-of 
	  select="$op1 mod 2"/></xslt:otherwise>
		</xslt:choose>
	</xslt:if>
</xslt:template>

This is a recursive template that starts from the left most character, iterating successively. At each position it'll take a modulus by 2 on the second param. If it is 1, then it displays one, otherwise it returns the modulus of the first param.

Ruben ??? offers the one I like! set the two variables to strings, e.g.

<xsl:variable name="operand1" select="'1010101110'"/>
	  <xsl:value-of 
	      select="translate(string(number($operand1) +
	         number($operand2)), '2', '1')"/>

Dimitre Novatchev offers

For any instance of a class of problems, requiring to perform an operation on two lists memberwise, the functional solution is the zipWith() function, which is implemented in FXSL.

A string is a special kind of list in XSLT, because we do not have a "char" datatype, and cannot represent a string as a list of chars.

This is why every FXSL function, which operates on lists (implemented as node-sets) has also a string analogue.

Here's the string analog of zipWith():

str-zipWith.xsl:
---------------

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:template name="str-zipWith">
    <xsl:param name="pFun" select="/.."/>
    <xsl:param name="pStr1" select="/.."/>
    <xsl:param name="pStr2" select="/.."/>

    <xsl:if test="string-length($pStr1) and string-length($pStr2)">
      <xsl:apply-templates select="$pFun">
        <xsl:with-param name="pArg1" select="substring($pStr1, 1, 1)"/>
        <xsl:with-param name="pArg2" select="substring($pStr2, 1, 1)"/>
      </xsl:apply-templates>

      <xsl:call-template name="str-zipWith">
        <xsl:with-param name="pFun" select="$pFun"/>
        <xsl:with-param name="pStr1" select="substring($pStr1, 2)"/>
        <xsl:with-param name="pStr2" select="substring($pStr2, 2)"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Now let's test it to solve your problem -- we need to "or" two strings containing only 0-s or 1-s:

test-str-zipWith.xsl:
--------------------

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 xmlns:myOr="f:myOr"
>
  <xsl:import href="str-zipWith.xsl"/>
  <!-- to be applied on numList.xml -->
  
  <xsl:output method="text"/>
  <myOr:myOr/>
  <xsl:template match="/">
    <xsl:variable name="vApos">'</xsl:variable>
    <xsl:variable name="vNL" select="'&#xA;'"/>
    <xsl:variable name="vFun" select="document('')/*/myOr:*[1]"/>

    <xsl:value-of select="concat('  ', $vApos, /*/*[1], $vApos, $vNL,
                                 'or',  $vNL,
                                 '  ', $vApos, /*/*[2], $vApos, $vNL,
                                 '=', $vNL,
                                 '  ', $vApos
                                 )"/>
    
    <xsl:call-template name="str-zipWith">
      <xsl:with-param name="pFun" select="$vFun"/>
      <xsl:with-param name="pStr1" select="/*/*[1]"/>
      <xsl:with-param name="pStr2" select="/*/*[2]"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="myOr:*">
    <xsl:param name="pArg1"/>
    <xsl:param name="pArg2"/>

    <xsl:value-of select="number(number($pArg1) or number($pArg2))"/>
  </xsl:template>

</xsl:stylesheet>

Let's apply the above transformation on the following source xml:

test-str-zipWith.xml:
--------------------

<test-str-zipWith>
  <str>001110101000</str>
  <str>101010101001</str>
</test-str-zipWith>


The result is:
-------------
  '001110101000'
or
  '101010101001'
=
  '101110101001

I can immediately implement "binary and", "xor", some kind of primitive encryption and ***any*** character-wise operation.

The versions of zipWith for more than two lists (and their string analogues) are left as an exercise to the reader :o)

27.

fibonacci

Michael Kay



> This is actually more a question of dynamic programming in XSL.
> If you can't remember the function it is:

> F0 = 0
> F1 = 1
> f(n)=f(n-1)+f(n-2) for n>=2

> is it possible to make a template that calculates this in linear time (I
> know it can be done in O(lg n) but linear is enogh) in xsl?
> The straightforward recursive method for calculating f(n) is clearly
> exponential in n.

> If it is possible I would prefer an example using memoization 
> because I need
> it to a similar problem I have.

You can do it in Saxon 7.x as:

<xsl:function name="m:fibonacci" saxon:memo-function="yes">
<xsl:param name="n" as="xs:integer">
<xsl:result as="xs:integer" 
   select="if ($n=0) then 0
           else if ($n=1) then 1
           else m:fibonacci($n-1) + m:fibonacci($n-2)"/>
</xsl:function>

In XSLT 1.0 you can do by calling a recursive template starting with the value 0, calling itself with the value $param+1 until the required parameter value is reached, each time supplying the values of the last two items in the sequence as parameters.

Mike Brown offers the 1.0 version

I have no idea if this meets your complexity requirements. I was just testing 4suite's tail recursion optimization with it a couple months ago.

<?xml version="1.0" encoding="utf-8"?>
<!--

  Fibonacci Numbers

  if n = 0, f(n) = 0
  if n = 1, f(n) = 1
  otherwise, f(n) = f(n-2) + f(n-1)

  tail-recursive version based on Scheme code by
  Ben Gum and John David Stone, at
  http://www.math.grin.edu/~gum/courses/151/readings/tail-recursion.xhtml
        
-->
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text" indent="no"/>

  <xsl:param name="n" select="100"/>

  <xsl:template match="/">
    <xsl:value-of select="concat('f(',$n,') = ')"/>
    <xsl:call-template name="fibo">
      <xsl:with-param name="n" select="$n"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="fibo">
    <xsl:param name="n" select="0"/>
    <xsl:call-template name="fibo-guts">
      <xsl:with-param name="current" select="0"/>
      <xsl:with-param name="next" select="1"/>
      <xsl:with-param name="remaining" select="$n"/>
    </xsl:call-template>
  </xsl:template>
    
  <xsl:template name="fibo-guts">
    <xsl:param name="current" select="0"/>
    <xsl:param name="next" select="0"/>
    <xsl:param name="remaining" select="0"/>
    <xsl:choose>
      <xsl:when test="$remaining = 0">
        <xsl:value-of select="$current"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="fibo-guts">
          <xsl:with-param name="current" select="$next"/>
          <xsl:with-param name="next" select="$current + $next"/>
          <xsl:with-param name="remaining" select="$remaining - 1"/>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

28.

union operator

Dimitre Novatchev.



>    <things>
>      <thing/>
>      <thing xml:lang="de"/>
>      <thing xml:lang="en"/>
>    </things>
>
>
> <value-of select="(thing[lang($mylang)] | thing)[1]"/>
>
>
> What I tried with this was to get a specific thing with the requested 
> language $mylang, and if no such element exists, to use the generic 
> thing without the xml:lang attribute. Obviously this does not work, I 
> always get the <thing/> first.
>
> Is there an elegant solution to this?

The following Xpath expression selects a node-set $ns1 if a condition $p is true and selects a node-set $ns2 if the same condition $p is not true:

     $ns1[$p]  |  $ns2[not($p)]

When it is known that the condition $p is that $ns1 must be non-empty, the above XPath expression can be simplified to the following:

     $ns1 | $ns2[not($ns1)]

In this specific case we'll have:

    /*/thing[lang($mylang)]    |
   /*/thing[1][not(/*/thing[lang($mylang)] )]

29.

Numbers to Words

Josh Canfield

There is probably a better way to do this, but here is my attempt... It will translate up to trillion, (999999999999999) after that it starts putting in the multiple instead of the name (1000000000000000). If I were going to do it again I might start by breaking the number string up into pieces so that math operations on large numbers doesn't limit the size of number supported...

Enjoy, Josh

===== xml.xml =====

<?xml version="1.0" encoding="ISO-8859-1"?>

<a>
  <num>12345678901234</num>
</a>

===== Output =====

<?xml version="1.0" encoding="utf-8"?>
<a>
   <numInWords>twelve trillion three hundred and forty-five billion six
hundred and seventy-eight million nine hundred and one thousand two hundred
and thirty-f our</numInWords> </a>

===== num-to-words.xsl =====

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
	<xsl:output method="xml" indent="yes" encoding="utf-8"/>


  <xsl:template match="/">
    <a><numInWords>
    <xsl:call-template name="num-to-word">
      <xsl:with-param name="value" select="a/num"/>
    </xsl:call-template>
    </numInWords></a>
  </xsl:template>

  <xsl:template name="num-to-word">
    <xsl:param name="value"/>
    <xsl:param name="multiple"/>

    <xsl:variable name="m">
      <xsl:choose>
        <xsl:when test="$multiple">
          <xsl:value-of select="$multiple"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:call-template name="get-multiple">
            <xsl:with-param name="value" select="$value"/>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:choose>
      
      <!-- partition, thousands, millions, billions, etc -->
      <xsl:when test="$value &gt; 9999">

        <xsl:variable name="part" select="floor($value div $m)"/>
        <xsl:variable name="r" select="$value - ($part * $m)"/>

        <xsl:call-template name="num-to-word">
          <xsl:with-param name="value" select="$part"/>
        </xsl:call-template>

        <xsl:text> </xsl:text><xsl:call-template
             name="word-map"><xsl:with-param name="val" 
              select="$m"/></xsl:call-template>
        <xsl:if test="$r">
          <xsl:text> </xsl:text>
          <xsl:call-template name="num-to-word">
            <xsl:with-param name="value" select="$r"/>
          </xsl:call-template>
        </xsl:if>

      </xsl:when>

      <!-- special case the teens -->
      <xsl:when test="$value &lt; 19">
        <xsl:call-template name="word-map">
          <xsl:with-param name="val" select="$value"/>
        </xsl:call-template>
      </xsl:when>

      <!-- handles values 20 - 9999 -->
      <xsl:otherwise>

        <xsl:variable name="part" select="floor($value div $m)"/>
        <xsl:variable name="r" select="$value - ($part * $m)"/>

        <xsl:choose>
          <xsl:when test="$m=10 and $part &gt; 1">
            <xsl:call-template name="word-map">
              <xsl:with-param name="val" select="$part*$m"/>
            </xsl:call-template>
          </xsl:when>

          <xsl:otherwise>
            <xsl:call-template name="word-map">
              <xsl:with-param name="val" select="$part"/>
            </xsl:call-template>

            <!-- add hundred, thousand, million, etc -->
            <xsl:if test="$m &gt; 10">
              <xsl:text> </xsl:text><xsl:call-template
          name="word-map"><xsl:with-param name="val" 
              select="$m"/></xsl:call-template>
            </xsl:if>

          </xsl:otherwise>
        </xsl:choose>

        <xsl:if test="$r">

          <xsl:choose>
            <!-- for: one hundred and five -->
            <xsl:when 
        test="$m = 100"><xsl:text> and </xsl:text></xsl:when>
            <!-- for: thirty-five -->
            <xsl:when 
      test="$m = 10"><xsl:text>-</xsl:text></xsl:when>
            <!-- for: one thousand one hundred -->
            <xsl:otherwise><xsl:text> </xsl:text></xsl:otherwise>
          </xsl:choose>

          <!-- translate the next part -->
          <xsl:call-template name="num-to-word">
            <xsl:with-param name="value" select="$r"/>
          </xsl:call-template>

        </xsl:if> 

      </xsl:otherwise>
    </xsl:choose>

  </xsl:template>

  <!-- determine the multiple for a number -->
  <xsl:template name="get-multiple">
    <xsl:param name="value"/>
    <xsl:param name="multiple" select="1"/>
    <xsl:param name="inc" select="10"/>
    <xsl:choose>
      <!-- at the thousand mark start multiplying by 1000 -->
      <xsl:when test="$value &gt; 999">
        <xsl:call-template name="get-multiple">
          <xsl:with-param name="value" select="floor($value div 1000)"/>
          <xsl:with-param name="multiple" select="$multiple * 1000"/>
          <xsl:with-param name="inc" select="1000"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:when test="$value &gt; $inc">
        <xsl:call-template name="get-multiple">
          <xsl:with-param name="value" select="floor($value div $inc)"/>
          <xsl:with-param name="multiple" select="$multiple * $inc"/>
          <xsl:with-param name="inc" select="$inc"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$multiple"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template name="word-map">
    <xsl:param name="val"/>
    <xsl:choose>
      <xsl:when test="$val = 1">one</xsl:when>
      <xsl:when test="$val = 2">two</xsl:when>
      <xsl:when test="$val = 3">three</xsl:when>
      <xsl:when test="$val = 4">four</xsl:when>
      <xsl:when test="$val = 5">five</xsl:when>
      <xsl:when test="$val = 6">six</xsl:when>
      <xsl:when test="$val = 7">seven</xsl:when>
      <xsl:when test="$val = 8">eight</xsl:when>
      <xsl:when test="$val = 9">nine</xsl:when>
      <xsl:when test="$val = 10">ten</xsl:when>
      <xsl:when test="$val = 11">eleven</xsl:when>
      <xsl:when test="$val = 12">twelve</xsl:when>
      <xsl:when test="$val = 13">thirteen</xsl:when>
      <xsl:when test="$val = 14">fourteen</xsl:when>
      <xsl:when test="$val = 15">fifteen</xsl:when>
      <xsl:when test="$val = 16">sixteen</xsl:when>
      <xsl:when test="$val = 17">seventeen</xsl:when>
      <xsl:when test="$val = 18">eighteen</xsl:when>
      <xsl:when test="$val = 19">nineteen</xsl:when>
      <xsl:when test="$val = 20">twenty</xsl:when>
      <xsl:when test="$val = 30">thirty</xsl:when>
      <xsl:when test="$val = 40">forty</xsl:when>
      <xsl:when test="$val = 50">fifty</xsl:when>
      <xsl:when test="$val = 60">sixty</xsl:when>
      <xsl:when test="$val = 70">seventy</xsl:when>
      <xsl:when test="$val = 80">eighty</xsl:when>
      <xsl:when test="$val = 90">ninety</xsl:when>
      <xsl:when test="$val = 100">hundred</xsl:when>
      <xsl:when test="$val = 1000">thousand</xsl:when>
      <xsl:when test="$val = 1000000">million</xsl:when>
      <xsl:when test="$val = 1000000000">billion</xsl:when>
      <xsl:when test="$val = 1000000000000">trillion</xsl:when>
      <xsl:otherwise><xsl:value-of select="$val"/></xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

30.

Random selection

Tom Passin, Peter Jacoby



> Is it possible to grab sections of an xml document
<chapter1><title></title><para></para></chapter1> 
> and randomize it with xslt? 
> Chapter 10 would be first, then 8 then another chapter in random order.

For randomization in XSLT, you should check out Dimitre Novatchev's excellent work on FXSL including a whole module for random number generation.

Casting the Dice with FXSL: Random Number Generation Functions in XSLT topxml

or

Yes, you could do that. I have a hack in the forthcoming OReilly book "XML Hacks" that shows a simple way to randomize with xslt. Basically, insert a string of random numbers into a node in the style sheet and index into it to get the random numbers. Or you could pass in a different random string as a global parameter each time you run the stylesheet.

31.

Bit Testing

George Cristian Bina



I want to test a attribute which has a number value, though numbers are in
string forms, if one of the bit of number is set.For example,

  <xsl:if test="bit 7 of Flags is 1">
    ...
  </xsl:if>

will be

<xsl:if test="(@Flags div 128) mod 2 = 1">

32.

XSLT for math computations

Dimtre Novatchev



> But Mark made the distinction, that it is not useful to use XSLT for 
> rather complex computations, in his example calculating prime numbers. 
> Though as a contrast, Dimitre posted an example, where XSLT is used 
> for a rather complex computation, which runs fast and successful. So 
> the question whether XSLT should be used for mathematical problems or 
> stuff like this is not fully clear yet. (For me, at least :))

Fundamentally wrong and incorrect question !

Not only one shouldn't be forbidden to use whatever tool they themselves have selected.

The prime number problem is not at all an isolated example.

FXSL provides pure XSLT implementation of some most fundamental math function groups such as:

  
  - exponential/logarithmic: 
    exp(), ln(). log(), log2(), log10(), sqrt(), pow(), ... etc.

  - trigonometric: sin(), cos(), tan(), cot(), secans(), cosecans()

  - inverse-trigonometric: the above with the prefix "arc"

  - Hyperbolic-trigonometric: the trigonometric fns with the prefix "h"

  - Finding the root of any equation of the form f(x) = 0, where f()
    is a continuos function of one real argument -- using the
    Newton-Raphston method and the binary search method.

  - Random number generator functions that produce a sequence of
    random numbers in a given interval and/or with a given
    distribution

  - some statistical functions

  - etc.

See this at: sourceforge and sourceforge

The other, factual reason is that using XSLT for math computations is simple, compact, elegant and fun.

For example, to get the sequence:

N^10, N = 1 to 10

one simply writes this one-line expression:

             f:map(f:flip(f:pow(),10), 1 to 10)

To get the sum of the tenth powers of all numbers from 1 to 10, one writes this one-liner:

     sum(f:map(f:flip(f:pow(),10), 1 to 10))

To get the tenth root of the above, this one-liner is used:

   f:pow(sum(f:map(f:flip(f:pow(),10), 1 to 10)), 0.1)

Here's the full result of a transformation. The expressions to the left of the "=" sign are the XPath expressions that are actually used in the transformation:

f:pow(2,5) = 32.000000498873234
-----------------------------------------------
f:flip(f:pow(), 2, 5) = 25.000000210097642
-----------------------------------------------
f:map(f:flip(f:pow(),2), 1 to 10) =
1
4.00000002487061
8.999999997225032
16.0000000654391
25.000000210097642
36.00000008032039
49.00000001473974
64.00000003117357
81.00000012870524
99.99999981533682
-----------------------------------------------
f:map(f:flip(f:pow(),10), 1 to 10) =
1
1024.000031977348
59048.99991721205
1.0485760215547918E6
9.765625412399698E6
6.046617668837886E7
2.8247524947463E8
1.0737418267311203E9
3.486784428898773E9
9.999999907930431E9
-----------------------------------------------
sum(f:map(f:flip(f:pow(),10), 1 to 10)) = 1.4914341865157238E10
-----------------------------------------------
f:pow(sum(f:map(f:flip(f:pow(),10), 1 to 10)), 0.1) =
10.407835264401298
-----------------------------------------------
f:log(2,4) = 0.5000000015112621
-----------------------------------------------
f:flip(f:log(), 2, 4) = 1.9999999939549515
-----------------------------------------------
f:map(f:flip(f:log(),2), 2 to 10) =
1
1.584962493378093
1.9999999939549515
2.321928090518739
2.58496249071745
2.807354909651913
2.9999999868509244
3.169924988315051
3.3219280785926495

The time it took to produce these results on my PC was: 469 milliseconds

The results are self-explanatory and it is very easy to learn how to compose arbitrary expressions using the basic math functions and operators of FXSL and XPath 2.0.

The combining "glue" is functional composition and using currying/partial application.

Note also, that as of present all (most important) XPath 2.0 functions and operators can be used as higher-order functions, by using a convenient FXSL wrapper with the same name.

For example, to produce the sequence:

   2*N, N = 1 to 10

the following XPath expression can be used:

   f:map(f:mult(2), 1 to 10)

In this expression f:mult() is(a wrapper of) the op:numeric-multiply operator as defined in W3C

f:mult(2) is the partial application of f:mult() with 2 as the first argument. It is itself a function that takes one argument and returns it multiplied by 2.

FXSL provides such wrappers for all (most important) XPath 2.0 functions and operators.

One final example. The following one-liner returns a sequence of numeric values approximating sqrt(2) with precision ranging from 0 to 13 digits after the decimal point:

    f:map(f:round-half-to-even(f:sqrt(2, 0.000001)), 0 to 13)

and the result is:

1
1.4
1.41
1.414
1.4142
1.41421
1.414214
1.4142136
1.41421356
1.414213562
1.4142135624
1.41421356237
1.414213562375
1.4142135623747

Hope this post makes it possible to be more clear about the usefulness of XSLT in math computations. It should certainly dispell the myth of XSLT being "slow" in these.

Fittingly, Michael emphasized in his post, that the argument, XSLT is slow, is not accurate for straightforward transformations any more, often the time for parsing the input and serializing the output is neglected.

33.

Why does 3 > 2 > 1 return false() and 1 < 2 < 3 return true()??

Jarno Elovirta



I was recently reminded that XPath 1.0 allows comparisons such as

  3 > 2 > 1

which return false.

While XPath 1.0 syntax allows consecutive comparisons, semantics differ from e.g. Python where evaluating 3 > 2 > 1 would return True.

Comparison operators are left associative, thus 3 > 2 > 1 can be rewritten to (3 > 2) > 1. When the comparison in the parentheses is evaluated, the expression is reduced to true() > 1. The greater-that comparison forces the boolean value to be cast to a number using the number() function, thus the expression reduces to 1 > 1 which in turn evaluates to false().

1 < 2 < 3 however reduces to 1 < 3 and returns true().

XPath 2.0 incompatibilities has this to say about the subject matter:

"Consecutive comparison operators such as A < B < C were supported in XPath 1.0, but are not permitted by the XPath 2.0 grammar. In most cases such comparisons in XPath 1.0 did not have the intuitive meaning, so it is unlikely that they have been widely used in practice. If such a construct is found, an XPath 2.0 processor will report a syntax error, and the construct can be rewritten as (A < B) < C"

34.

Sum over computed nodes

Michael Kay

You may have noticed there is another thread running on the same subject - how to sum over values that are computed from those held in the nodes, rather than the actual string-values of the nodes. The solutions on offer include:

(a) create a temporary tree containing nodes holding the values directly, then sum over the nodes in that temporary tree
(b) a recursive named template
(c) the f:map function in FXSL
(d) the saxon:sum() extension function in Saxon 6.5.3
(e) In XSLT 2.0, sum(for $x in $nodes return number(tokenize($x, ' ')))
(f) In Schema-aware XSLT 2.0, if the type of your gml:Pos nodes is list of numbers, then the sum() function will do the job directly.

35.

Count unique terms

David Carlisle


I have an XML document with the following structure:

<inventory>
 <row>
   <hostmame>AA</hostname>
   <serial>A</serial>
 </row>
 <row>
   <hostmame>BB</hostname>
   <serial>B</serial>
 </row>
 <row>
    ...
 </row>
</inventory>

I defined a key such as:
<xsl:key name="systems" match="/inventory/row/serial" use="."/>

How do I count the number of elements in the key "systems" seeing that several row could have the same "serial" value ? The purpose being to compute the number of unique serial values found in the XML document...

That's the basis of the "muenchian grouping" technique:

count(/inventory/row/serial[generate-id()=generate-id(key('systems',.)[1])])

Andrew offers

count(distinct-values(/inventory/row/serial)) if you are using XSLT 2.0

36.

Maths in xsl

Andrew Welch


> I have the following XML file, which represents expressions with "+" and "*"
>
> artihmentic operands:
> <m value="10">
>   <i value="5"/>
>   <m value="10">
>      <i value="2"/>
>      <i value="2"/>
>      <i value="2"/>
>   </m>
> </m>
>
> This represents the following expression:
>
> 10 * (5 + (10 * (2 + 2 + 2)))
>
> I want to compute the expression's value (for the given example, the
> result should be 650).
>
> Can anyone help me achieve this through a single recursive template?

I think you are better off with two templates:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

<xsl:template match="m">
   <xsl:variable name="sum">
       <xsl:apply-templates select="*[1]"/>
   </xsl:variable>
   <xsl:choose>
       <xsl:when test="$sum != ''">
           <xsl:value-of select="@value * $sum"/>
       </xsl:when>
       <xsl:otherwise>
           <xsl:value-of select="@value"/>
       </xsl:otherwise>
   </xsl:choose>
</xsl:template>

<xsl:template match="i">
   <xsl:variable name="sum">
       <xsl:apply-templates select="following-sibling::*[1]"/>
   </xsl:variable>
   <xsl:choose>
       <xsl:when test="$sum != ''">
           <xsl:value-of select="@value + $sum"/>
       </xsl:when>
       <xsl:otherwise>
           <xsl:value-of select="@value"/>
       </xsl:otherwise>
   </xsl:choose>
</xsl:template>

</xsl:stylesheet>

If you are using XSLT 2.0 then you can drop the choose/when for the more terse if then else:

select="if ($sum != '') then (@value + $sum) else (@value)"

37.

mod 10 xpath vs math

David Carlisle



> why is:
> -(-foo mod 10)
>
> not the same as:
>
> foo mod 10

the answer: it is the same. Mod in xpath keeps the sign, so (for example) -(-9 mod 10) is 9 as is 9 mod 10.

To test if $x and $y are equal mod 10 you need to test ($x -$y) mod 10 = 0 rather than ($x mod 10) = ($y mod 10) for the reasons given.

In the real world (that is, in mathematics) it is, also the mod function doesn't return an integer but rather an element of the finite set Z_n (which is how come 0 = 10 in the first place) however in most computer languages, and in particular in XPath the designers define mod not to be an operation on the set Z_n but rather an operation on Z (The integers) that is "remainder after division, and that retains the sign, so in XPath

-9 mod 10 = -9
1 mod 10 = 1
so even though 1 and -9 are equal mod 10, it's not true that
(1 mod 10) = (-9 mod 10)
in Xpath, but it is true that
(1 - -9 ) mod 10 = 0

which means that to check $x and $y are equal mod 10 you can use ($x - $y) mod 10 = 0 as you don't care which coset representative the mod operator uses, but if you want to generate the coset representative for -$x you can't just use -$x mod 10 you have to use (10 - $x) mod 10 to get the positive representative, hence my comment that it matters whether you are just checking a given checkdigit, or if you are calculting a digit and need to ensure you end up in the range 0 to 1.