2 <xsl:stylesheet version="1.0"
3 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
4 xmlns:doc="http://xsltsl.org/xsl/documentation/1.0"
5 xmlns:math="http://xsltsl.org/math"
6 exclude-result-prefixes="doc math">
8 <doc:reference xmlns="">
10 <releaseinfo role="meta">
11 $Id: math.xsl 3991 2004-11-10 06:51:55Z balls $
14 <surname>Ball</surname>
15 <firstname>Steve</firstname>
20 <holder>Steve Ball</holder>
24 <title>Math Module</title>
28 <title>Introduction</title>
30 <para>This module provides mathematical functions.</para>
36 <doc:template name="math:power" xmlns="">
37 <refpurpose>Power</refpurpose>
40 <para>Raises a number to a power.</para>
48 <para>The base number. Must be a number.</para>
54 <para>The power to raise the number to. Must be an integer.</para>
61 <para>Returns base multiplied by itself power times. If the base or power are not numbers or if the power is fractional then an empty string is returned.</para>
65 <xsl:template name="math:power">
66 <xsl:param name="base"/>
67 <xsl:param name="power"/>
70 <xsl:when test='$power = "0" and $base = "0"'>
71 <xsl:text>1</xsl:text>
73 <xsl:when test='$power = "0" and number($base)'>
74 <xsl:text>1</xsl:text>
76 <xsl:when test='$power = "0" and not(number($base))'/>
77 <xsl:when test='$base = "0" and number($power)'>
78 <xsl:text>0</xsl:text>
81 <xsl:when test='not(number($base)) or not(number($power))'/>
83 <xsl:when test='floor(number($power)) != number($power)'/>
85 <xsl:when test='number($power) < 0'>
86 <xsl:variable name='x'>
87 <xsl:call-template name='math:power'>
88 <xsl:with-param name='base' select='$base'/>
89 <xsl:with-param name='power' select='-1 * $power'/>
92 <xsl:value-of select='1 div $x'/>
95 <xsl:when test='number($power) = 1'>
96 <xsl:value-of select='$base'/>
99 <xsl:when test='number($power) > 0'>
100 <xsl:variable name='x'>
101 <xsl:call-template name='math:power'>
102 <xsl:with-param name='base' select='$base'/>
103 <xsl:with-param name='power' select='$power - 1'/>
106 <xsl:value-of select='$base * $x'/>
112 <doc:template name="math:abs" xmlns="">
113 <refpurpose>Absolute Value</refpurpose>
116 <para>Absolute value of a number.</para>
124 <para>The number. Must be a number.</para>
131 <para>Returns the absolute value of the number.</para>
135 <xsl:template name="math:abs">
136 <xsl:param name="number"/>
139 <xsl:when test='$number < 0'>
140 <xsl:value-of select='$number * -1'/>
142 <xsl:when test='$number >= 0'>
143 <xsl:value-of select='$number'/>
148 <doc:template name="math:cvt-hex-decimal" xmlns="">
149 <refpurpose>Conversion</refpurpose>
152 <para>Converts a hexidecimal value to a decimal value.</para>
160 <para>The hexidecimal number. Must be a number in hexidecimal format.</para>
167 <para>Returns the value as a decimal string. If the value is not a number then a NaN value is returned.</para>
171 <xsl:template name="math:cvt-hex-decimal">
172 <xsl:param name="value"/>
175 <xsl:when test='$value = ""'/>
177 <xsl:when test='string-length($value) = 1'>
178 <xsl:call-template name='math:cvt-hex-decimal-digit'>
179 <xsl:with-param name='digit' select='$value'/>
183 <xsl:variable name='first-digit'>
184 <xsl:call-template name='math:cvt-hex-decimal-digit'>
185 <xsl:with-param name='digit' select='substring($value, 1, 1)'/>
188 <xsl:variable name='remainder'>
189 <xsl:call-template name='math:cvt-hex-decimal'>
190 <xsl:with-param name='value' select='substring($value, 2)'/>
194 <xsl:value-of select='$first-digit * 16 + $remainder'/>
199 <xsl:template name='math:cvt-hex-decimal-digit'>
200 <xsl:param name='digit' select='0'/>
202 <xsl:when test='$digit <= 9'>
203 <xsl:value-of select='$digit'/>
205 <xsl:when test='$digit = "a" or $digit = "A"'>10</xsl:when>
206 <xsl:when test='$digit = "b" or $digit = "B"'>11</xsl:when>
207 <xsl:when test='$digit = "c" or $digit = "C"'>12</xsl:when>
208 <xsl:when test='$digit = "d" or $digit = "D"'>13</xsl:when>
209 <xsl:when test='$digit = "e" or $digit = "E"'>14</xsl:when>
210 <xsl:when test='$digit = "f" or $digit = "F"'>15</xsl:when>
214 <doc:template name="math:cvt-decimal-hex" xmlns="">
215 <refpurpose>Conversion</refpurpose>
218 <para>Converts a decimal value to a hexidecimal value.</para>
226 <para>The decimal number.</para>
233 <para>Returns the value as a hexidecimal string (lowercase). If the value is not a number then a NaN value is returned.</para>
237 <xsl:template name="math:cvt-decimal-hex">
238 <xsl:param name="value"/>
241 <xsl:when test='$value = "0"'>0</xsl:when>
242 <xsl:when test='not(number($value))'>NaN</xsl:when>
244 <xsl:when test='$value div 16 >= 1'>
245 <xsl:call-template name='math:cvt-decimal-hex'>
246 <xsl:with-param name='value' select='floor($value div 16)'/>
248 <xsl:call-template name='math:cvt-decimal-hex'>
249 <xsl:with-param name='value' select='$value mod 16'/>
252 <xsl:when test='$value = 10'>a</xsl:when>
253 <xsl:when test='$value = 11'>b</xsl:when>
254 <xsl:when test='$value = 12'>c</xsl:when>
255 <xsl:when test='$value = 13'>d</xsl:when>
256 <xsl:when test='$value = 14'>e</xsl:when>
257 <xsl:when test='$value = 15'>f</xsl:when>
259 <xsl:value-of select='$value'/>
264 <doc:template name="math:ordinal" xmlns="">
265 <refpurpose>Ordinal number</refpurpose>
268 <para>Gives the ordinal number of a given counting number. For example, 1 becomes "1st".</para>
276 <para>An integer number.</para>
283 <para>Returns the number with an ordinal suffix.</para>
287 <xsl:template name="math:ordinal">
288 <xsl:param name="number"/>
291 <xsl:when test='$number < 0'/>
293 <xsl:value-of select='$number'/>
295 <xsl:when test='$number = 11 or $number = 12 or $number = 13'>th</xsl:when>
296 <xsl:when test='$number mod 10 = 1'>st</xsl:when>
297 <xsl:when test='$number mod 10 = 2'>nd</xsl:when>
298 <xsl:when test='$number mod 10 = 3'>rd</xsl:when>
299 <xsl:otherwise>th</xsl:otherwise>
306 <doc:template name="math:ordinal-as-word" xmlns="">
307 <refpurpose>Returns an ordinal number</refpurpose>
310 <para>This template returns the ordinal number for a given counting number as a word. For example "first" for 1.</para>
311 <para>Only handles numbers less than 10000000 (ten million).</para>
319 <para>The counting number.</para>
323 <term>conjunctive</term>
325 <para>Whether to add the word "and" to the result, for example "one hundred and first" rather than "one hundred first". Default is "yes".</para>
332 <para>Returns the ordinal number as a string.</para>
336 <xsl:template name="math:ordinal-as-word">
337 <xsl:param name="number" select="0"/>
338 <xsl:param name='conjunctive' select='"yes"'/>
339 <xsl:param name='preceding' select='0'/>
342 <xsl:when test='$preceding = 1 and $number = 0'/>
343 <xsl:when test='$number = 0'>zeroth</xsl:when>
345 <xsl:when test="$number < 1 or $number != floor($number)"/>
347 <xsl:when test='$number = 1'>
348 <xsl:if test='$preceding = 1'> and </xsl:if>
349 <xsl:text>first</xsl:text>
351 <xsl:when test='$number = 2'>
352 <xsl:if test='$preceding = 1'> and </xsl:if>
353 <xsl:text>second</xsl:text>
355 <xsl:when test='$number = 3'>
356 <xsl:if test='$preceding = 1'> and </xsl:if>
357 <xsl:text>third</xsl:text>
359 <xsl:when test='$number = 4'>
360 <xsl:if test='$preceding = 1'> and </xsl:if>
361 <xsl:text>fourth</xsl:text>
363 <xsl:when test='$number = 5'>
364 <xsl:if test='$preceding = 1'> and </xsl:if>
365 <xsl:text>fifth</xsl:text>
367 <xsl:when test='$number = 6'>
368 <xsl:if test='$preceding = 1'> and </xsl:if>
369 <xsl:text>sixth</xsl:text>
371 <xsl:when test='$number = 7'>
372 <xsl:if test='$preceding = 1'> and </xsl:if>
373 <xsl:text>seventh</xsl:text>
375 <xsl:when test='$number = 8'>
376 <xsl:if test='$preceding = 1'> and </xsl:if>
377 <xsl:text>eighth</xsl:text>
379 <xsl:when test='$number = 9'>
380 <xsl:if test='$preceding = 1'> and </xsl:if>
381 <xsl:text>ninth</xsl:text>
383 <xsl:when test='$number = 10'>
384 <xsl:if test='$preceding = 1'> and </xsl:if>
385 <xsl:text>tenth</xsl:text>
387 <xsl:when test='$number = 11'>
388 <xsl:if test='$preceding = 1'> and </xsl:if>
389 <xsl:text>eleventh</xsl:text>
391 <xsl:when test='$number = 12'>
392 <xsl:if test='$preceding = 1'> and </xsl:if>
393 <xsl:text>twelveth</xsl:text>
395 <xsl:when test='$number = 13'>
396 <xsl:if test='$preceding = 1'> and </xsl:if>
397 <xsl:text>thirteenth</xsl:text>
399 <xsl:when test='$number = 14'>
400 <xsl:if test='$preceding = 1'> and </xsl:if>
401 <xsl:text>fourteenth</xsl:text>
403 <xsl:when test='$number = 15'>
404 <xsl:if test='$preceding = 1'> and </xsl:if>
405 <xsl:text>fifteenth</xsl:text>
407 <xsl:when test='$number = 16'>
408 <xsl:if test='$preceding = 1'> and </xsl:if>
409 <xsl:text>sixteenth</xsl:text>
411 <xsl:when test='$number = 17'>
412 <xsl:if test='$preceding = 1'> and </xsl:if>
413 <xsl:text>seventeenth</xsl:text>
415 <xsl:when test='$number = 18'>
416 <xsl:if test='$preceding = 1'> and </xsl:if>
417 <xsl:text>eighteenth</xsl:text>
419 <xsl:when test='$number = 19'>
420 <xsl:if test='$preceding = 1'> and </xsl:if>
421 <xsl:text>nineteenth</xsl:text>
423 <xsl:when test='$number = 20'>
424 <xsl:if test='$preceding = 1'> and </xsl:if>
425 <xsl:text>twentieth</xsl:text>
427 <xsl:when test='$number = 30'>
428 <xsl:if test='$preceding = 1'> and </xsl:if>
429 <xsl:text>thirtieth</xsl:text>
431 <xsl:when test='$number = 40'>
432 <xsl:if test='$preceding = 1'> and </xsl:if>
433 <xsl:text>fortieth</xsl:text>
435 <xsl:when test='$number = 50'>
436 <xsl:if test='$preceding = 1'> and </xsl:if>
437 <xsl:text>fiftieth</xsl:text>
439 <xsl:when test='$number = 60'>
440 <xsl:if test='$preceding = 1'> and </xsl:if>
441 <xsl:text>sixtieth</xsl:text>
443 <xsl:when test='$number = 70'>
444 <xsl:if test='$preceding = 1'> and </xsl:if>
445 <xsl:text>seventieth</xsl:text>
447 <xsl:when test='$number = 80'>
448 <xsl:if test='$preceding = 1'> and </xsl:if>
449 <xsl:text>eightieth</xsl:text>
451 <xsl:when test='$number = 90'>
452 <xsl:if test='$preceding = 1'> and </xsl:if>
453 <xsl:text>ninetieth</xsl:text>
456 <xsl:when test='$number mod 1000000 = 0'>
457 <xsl:call-template name='math:number-as-word'>
458 <xsl:with-param name='number' select='floor($number div 1000000)'/>
460 <xsl:text> millionth</xsl:text>
462 <xsl:when test='$number < 1000000 and $number mod 1000 = 0'>
463 <xsl:if test='$preceding = 1 and $conjunctive'> and </xsl:if>
464 <xsl:call-template name='math:number-as-word'>
465 <xsl:with-param name='number' select='floor($number div 1000)'/>
467 <xsl:text> thousandth</xsl:text>
469 <xsl:when test='$number < 1000 and $number mod 100 = 0'>
470 <xsl:if test='$preceding = 1 and $conjunctive'> and </xsl:if>
471 <xsl:call-template name='math:number-as-word'>
472 <xsl:with-param name='number' select='floor($number div 100)'/>
474 <xsl:text> hundredth</xsl:text>
477 <xsl:when test='$number > 1000000'>
478 <xsl:if test='$preceding = 1'>
479 <xsl:text> </xsl:text>
480 <xsl:if test='$conjunctive'>and </xsl:if>
482 <xsl:call-template name='math:number-as-word'>
483 <xsl:with-param name='number' select='floor($number div 1000000) * 1000000'/>
487 test='(floor(floor(($number mod 1000000) + 0.1) div 100000) > 0 and $number mod 100000 > 0) or
488 (floor(floor(($number mod 100000) + 0.1) div 10000) > 0 and $number mod 10000 > 0) or
489 (floor(floor(($number mod 10000) + 0.1) div 1000) > 0 and $number mod 1000 > 0) or
490 (floor(floor(($number mod 1000) + 0.1) div 100) > 0 and $number mod 100 > 0) or
491 (floor(floor(($number mod 100) + 0.1) div 10) > 0 and $number mod 10 > 0 and $number mod 100 > 20)'>
492 <xsl:text> </xsl:text>
493 <xsl:call-template name='math:ordinal-as-word'>
494 <xsl:with-param name='number' select='floor(($number mod 1000000) + 0.1)'/>
495 <xsl:with-param name='conjunctive' select='$conjunctive'/>
496 <xsl:with-param name='preceding' select='0'/>
500 <xsl:call-template name='math:ordinal-as-word'>
501 <xsl:with-param name='number' select='floor(($number mod 1000000) + 0.1)'/>
502 <xsl:with-param name='conjunctive' select='$conjunctive'/>
503 <xsl:with-param name='preceding' select='1'/>
508 <xsl:when test='$number > 1000'>
509 <xsl:if test='$preceding = 1'>
510 <xsl:text> </xsl:text>
511 <xsl:if test='$conjunctive'>and </xsl:if>
513 <xsl:call-template name='math:number-as-word'>
514 <xsl:with-param name='number' select='floor($number div 1000) * 1000'/>
515 <xsl:with-param name='conjunctive' select='$conjunctive'/>
518 <xsl:when test='floor(floor(($number mod 1000) + 0.1) div 100) > 0'>
519 <xsl:text> </xsl:text>
520 <xsl:call-template name='math:ordinal-as-word'>
521 <xsl:with-param name='number' select='floor(($number mod 1000) + 0.1)'/>
522 <xsl:with-param name='conjunctive' select='$conjunctive'/>
523 <xsl:with-param name='preceding' select='0'/>
527 <xsl:call-template name='math:ordinal-as-word'>
528 <xsl:with-param name='number' select='floor(($number mod 1000) + 0.1)'/>
529 <xsl:with-param name='conjunctive' select='$conjunctive'/>
530 <xsl:with-param name='preceding' select='1'/>
535 <xsl:when test='$number > 100'>
536 <xsl:if test='$preceding = 1'>
537 <xsl:text> </xsl:text>
538 <xsl:if test='$conjunctive'>and </xsl:if>
540 <xsl:call-template name='math:number-as-word'>
541 <xsl:with-param name='number' select='floor($number div 100) * 100'/>
543 <xsl:call-template name='math:ordinal-as-word'>
544 <xsl:with-param name='number' select='floor(($number mod 100) + 0.1)'/>
545 <xsl:with-param name='conjunctive' select='$conjunctive'/>
546 <xsl:with-param name='preceding' select='1'/>
550 <xsl:when test='$number > 20'>
551 <xsl:if test='$preceding = 1'>
552 <xsl:text> </xsl:text>
553 <xsl:if test='$conjunctive'>and </xsl:if>
555 <xsl:call-template name='math:number-as-word'>
556 <xsl:with-param name='number' select='floor($number div 10) * 10'/>
558 <xsl:text> </xsl:text>
559 <xsl:call-template name='math:ordinal-as-word'>
560 <xsl:with-param name='number' select='floor(($number mod 10) + 0.1)'/>
561 <xsl:with-param name='conjunctive' select='$conjunctive'/>
569 <doc:template name="math:number-as-word" xmlns="">
570 <refpurpose>Returns a number as a word</refpurpose>
573 <para>This template returns the word for a given integer number, for example "one" for 1.</para>
574 <para>Only handles numbers less than 10000000 (ten million).</para>
582 <para>The counting number.</para>
586 <term>conjunctive</term>
588 <para>Adds the word "and" where appropriate, for example.</para>
595 <para>Returns the number as a string.</para>
599 <xsl:template name="math:number-as-word">
600 <xsl:param name="number" select="0"/>
601 <xsl:param name='conjunctive' select='true()'/>
605 <xsl:when test='$number = 0'>zero</xsl:when>
607 <xsl:when test='$number < 0'>
608 <xsl:text>minus </xsl:text>
609 <xsl:call-template name='math:number-as-word'>
610 <xsl:with-param name='number' select='-1 * $number'/>
614 <xsl:when test="$number != floor($number)"/>
616 <xsl:when test='$number mod 1000000 = 0'>
617 <xsl:call-template name='math:number-as-word'>
618 <xsl:with-param name='number' select='floor($number div 1000000)'/>
620 <xsl:text> million</xsl:text>
622 <xsl:when test='$number >= 1000000'>
623 <xsl:call-template name='math:number-as-word'>
624 <xsl:with-param name='number' select='floor($number div 1000000)'/>
626 <xsl:text> million </xsl:text>
627 <xsl:call-template name='math:number-as-word'>
628 <xsl:with-param name='number' select='floor(($number mod 1000000) + 0.1)'/>
631 <xsl:when test='$number mod 1000 = 0'>
632 <xsl:call-template name='math:number-as-word'>
633 <xsl:with-param name='number' select='floor($number div 1000)'/>
635 <xsl:text> thousand</xsl:text>
637 <xsl:when test='$number >= 1000'>
638 <xsl:call-template name='math:number-as-word'>
639 <xsl:with-param name='number' select='floor($number div 1000)'/>
641 <xsl:text> thousand </xsl:text>
642 <xsl:if test='$conjunctive and floor(floor(($number mod 1000) + 0.1) div 100) = 0'>and </xsl:if>
643 <xsl:call-template name='math:number-as-word'>
644 <xsl:with-param name='number' select='floor(($number mod 1000) + 0.1)'/>
647 <xsl:when test='$number mod 100 = 0'>
648 <xsl:call-template name='math:number-as-word'>
649 <xsl:with-param name='number' select='floor($number div 100)'/>
651 <xsl:text> hundred</xsl:text>
653 <xsl:when test='$number >= 100'>
654 <xsl:call-template name='math:number-as-word'>
655 <xsl:with-param name='number' select='floor($number div 100)'/>
657 <xsl:text> hundred </xsl:text>
658 <xsl:if test='$conjunctive'>and </xsl:if>
659 <xsl:call-template name='math:number-as-word'>
660 <xsl:with-param name='number' select='floor(($number mod 100) + 0.1)'/>
664 <xsl:when test='$number = 1'>one</xsl:when>
665 <xsl:when test='$number = 2'>two</xsl:when>
666 <xsl:when test='$number = 3'>three</xsl:when>
667 <xsl:when test='$number = 4'>four</xsl:when>
668 <xsl:when test='$number = 5'>five</xsl:when>
669 <xsl:when test='$number = 6'>six</xsl:when>
670 <xsl:when test='$number = 7'>seven</xsl:when>
671 <xsl:when test='$number = 8'>eight</xsl:when>
672 <xsl:when test='$number = 9'>nine</xsl:when>
673 <xsl:when test='$number = 10'>ten</xsl:when>
674 <xsl:when test='$number = 11'>eleven</xsl:when>
675 <xsl:when test='$number = 12'>twelve</xsl:when>
676 <xsl:when test='$number = 13'>thirteen</xsl:when>
677 <xsl:when test='$number = 14'>fourteen</xsl:when>
678 <xsl:when test='$number = 15'>fifteen</xsl:when>
679 <xsl:when test='$number = 16'>sixteen</xsl:when>
680 <xsl:when test='$number = 17'>seventeen</xsl:when>
681 <xsl:when test='$number = 18'>eighteen</xsl:when>
682 <xsl:when test='$number = 19'>nineteen</xsl:when>
683 <xsl:when test='$number = 20'>twenty</xsl:when>
684 <xsl:when test='$number = 30'>thirty</xsl:when>
685 <xsl:when test='$number = 40'>forty</xsl:when>
686 <xsl:when test='$number = 50'>fifty</xsl:when>
687 <xsl:when test='$number = 60'>sixty</xsl:when>
688 <xsl:when test='$number = 70'>seventy</xsl:when>
689 <xsl:when test='$number = 80'>eighty</xsl:when>
690 <xsl:when test='$number = 90'>ninety</xsl:when>
692 <xsl:when test='$number < 100'>
693 <xsl:call-template name='math:number-as-word'>
694 <xsl:with-param name='number' select='floor($number div 10) * 10'/>
696 <xsl:text> </xsl:text>
697 <xsl:call-template name='math:number-as-word'>
698 <xsl:with-param name='number' select='floor(($number mod 10) + 0.1)'/>