2
\$\begingroup\$

I'm creating a client-side pie chart in XSLT 1.0, without extensions or external dependencies (i.e., cannot use FXSLT, EXSLT, JavaScript, etc.).

The code is slow; at least fifteen iterations are necessary to get the correct precision required for the angles used by the pie chart. (I'm looking for help that doesn't involve loop unrolling, if possible.)

How would you speed calculating the sine function?

<xsl:template name="power">
  <xsl:param name="n"/>
  <xsl:param name="exponent"/>
  <xsl:param name="result" select="1" />
  <xsl:choose>
    <xsl:when test="$exponent = 0">1</xsl:when>
    <xsl:when test="$exponent = 1">
      <xsl:value-of select="$result * $n"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="power">
        <xsl:with-param name="n" select="$n"/>
        <xsl:with-param name="exponent" select="$exponent - 1"/>
        <xsl:with-param name="result" select="$result * $n"/>
      </xsl:call-template>                          
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="factorial">
  <xsl:param name="n" select="1" />

  <xsl:choose>
    <xsl:when test="$n &lt;= 1">1</xsl:when>
    <xsl:otherwise>
      <xsl:variable name="f">
        <xsl:call-template name="factorial">
           <xsl:with-param name="n" select="$n - 1" />
        </xsl:call-template>
      </xsl:variable>
      <xsl:value-of select="$n * $f" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- sin(x) = x - x^3/3! + x^5/5! - x^7/7! ... -->
<xsl:template name="sin">
  <xsl:param name="degrees" />
  <xsl:param name="radians" select="$degrees * $PI div 180" />
  <xsl:param name="iter" select="1" />
  <xsl:param name="maxiter" select="15" />
  <xsl:param name="result" select="0" />

  <xsl:variable name="taylor" select="($iter - 1) * 2 + 1" />
  <xsl:variable name="numerator">
    <xsl:call-template name="power">
      <xsl:with-param name="n" select="$radians" />
      <xsl:with-param name="exponent" select="$taylor" />
    </xsl:call-template>
  </xsl:variable>
  <xsl:variable name="denominator">
    <xsl:call-template name="factorial">
      <xsl:with-param name="n" select="$taylor" />
    </xsl:call-template>
  </xsl:variable>

  <xsl:variable name="term">
    <xsl:choose>
      <xsl:when test="$iter mod 2 = 1">
        <xsl:value-of select="$numerator div $denominator" />
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="-1 * $numerator div $denominator" /></xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="$iter &lt; $maxiter">
      <xsl:call-template name="sin">
        <xsl:with-param name="radians" select="$radians" />
        <xsl:with-param name="iter" select="$iter + 1" />
        <xsl:with-param name="result" select="$result + $term" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise><xsl:value-of select="$result" /></xsl:otherwise>
  </xsl:choose>
</xsl:template>

A similar question exists for C, but loop unrolling is awkward in XSLT due to its immutable variables.

\$\endgroup\$
  • \$\begingroup\$ If your goal is to create a pie chart, why not post the XSLT and example XML input for that? Perhaps we could reduce the use of sine altogether. \$\endgroup\$ – 200_success Aug 1 '14 at 19:10
3
\$\begingroup\$

Do not use power and factorial. Notice that

$$x - \frac{x^3}{3!} + \frac{x^5}{5!} - \frac{x^7}{7!} + \ldots = x(1 - \frac{x^2}{2*3}(1 - \frac{x^2}{4*5}(1 - \frac{x^2}{6*7}(1 - \ldots))) \ldots) $$

Now calculate it backwards from the innermost term:

  <!-- sin(x) = x(1 - (x^2/2*3)(1 - (x^2/4*5)(1 - (x^2/6*7)))) -->
  <xsl:template name="sin">
    <xsl:param name="degrees" />
    <xsl:param name="radians" select="$degrees * $PI div 180" />
    <xsl:param name="iter" select="31" />
    <xsl:param name="result" select="1" />

    <xsl:variable name="term" select="($radians * $radians) div (($iter - 1) * $iter)" />

    <xsl:choose>
      <xsl:when test="$iter &gt; 2">
        <xsl:call-template name="sin">
          <xsl:with-param name="radians" select="$radians" />
          <xsl:with-param name="iter" select="$iter - 2" />
          <xsl:with-param name="result" select="1 - ($term * $result)" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="$radians * $result" /></xsl:otherwise>
    </xsl:choose>
  </xsl:template>
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.