Listing 1: schema.xml

<?xml version="1.0" encoding="UTF-8"?> <schema> <!--
=============================================
SCHEMA.XML - created by Ed Gimzewski, Fitch Ratings, 2005-01-12
=============================================
A pseudo schema that has the basic information that XBRL would hold in an XSchema file plus
 a formula linkbase.
-->
<!-- Schema for simple case study -->
<!-- items for input data-->
<item id="F1" type="input"/>
<item id="F2" type="input"/>
<item id="F3" type="input"/>
<!-- items for calculated data.-->
<item id="F10" formula="$F1 + $F2" type="calc"/>
<item id="F11" formula="($F10 + $F3) div $F2" type="calc"/>
<item id="F12" formula="($F10 - $F10_prev) div $F2" type="calc"/>
<item id="F13" formula="if( ($F10 - $F10_prev) gt 0.01*$F10_prev )
then ($F10 + $F3) else ($F10)" type="calc"/>
</schema>

Listing 2: functions.xslt

<?xml version="1.0" encoding="UTF-8"?><!--
=====================================================
FUNCTIONS.XSLT

Created by transforming SCHEMA.XML with COMPILER.XSLT
=====================================================
-->
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xdt="http://www.w3.org/2003/11/xpath-datatypes"
   xmlns:compiler="http://www.fitchratings.com/compiler"
    xmlns:formula="http://www.fitchratings.com/formula"
	 xmlns:helper="http://www.fitchratings.com/helper" version="2.0">
<xsl:output indent="yes" method="xml" encoding="UTF-8"/>
<!--================CONSTANTS AND KEYS================-->
<xsl:variable name="mod_prev" select="'prev'"/>
<xsl:variable name="this_doc" select="/instance"/>
<xsl:key name="key_get_input_val" match="/instance/*
[name() ne 'context']" use="concat(@context, '|', name())"/>
<xsl:key name="key_get_contextFromID" match="/instance/context" use="@id"/>
<xsl:key name="key_get_contextFromEndDate" match="/instance/context"
 use="xs:date(period/endDate)"/>
<!--================FORMULA FUNCTIONS================-->
<!--================Simple formulas================-->
<!--=========================-->
<xsl:function name="formula:F10" as="xs:double">
<xsl:param name="context_id" as="xs:string?"/>
<xsl:choose>
<xsl:when test="empty($context_id)">
<xsl:sequence select="xs:double(0)"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="F1" select="helper:get_input_value
( 'F1', $context_id)" as="xs:double"/>
<xsl:variable name="F2" select="helper:get_input_value
( 'F2', $context_id)" as="xs:double"/>
<xsl:sequence select="$F1 + $F2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--=========================-->
<xsl:function name="formula:F11" as="xs:double">
<xsl:param name="context_id" as="xs:string?"/>
<xsl:choose>
<xsl:when test="empty($context_id)">
<xsl:sequence select="xs:double(0)"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="F10" select="formula:F10
( $context_id)" as="xs:double"/>
<xsl:variable name="F3" select="helper:get_input_value
( 'F3', $context_id)" as="xs:double"/>
<xsl:variable name="F2" select="helper:get_input_value
( 'F2', $context_id)" as="xs:double"/>
<xsl:sequence select="($F10 + $F3) div $F2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--=========================-->
<xsl:function name="formula:F12" as="xs:double">
<xsl:param name="context_id" as="xs:string?"/>
<xsl:choose>
<xsl:when test="empty($context_id)">
<xsl:sequence select="xs:double(0)"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="F10" select="formula:F10
( $context_id)" as="xs:double"/>
<xsl:variable name="F10_prev" select="formula:F10
(helper:get_previous($context_id))" as="xs:double"/>
<xsl:variable name="F2" select="helper:get_input_value
( 'F2', $context_id)" as="xs:double"/>
<xsl:sequence select="($F10 - $F10_prev) div $F2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--=========================-->
<xsl:function name="formula:F13" as="xs:double">
<xsl:param name="context_id" as="xs:string?"/>
<xsl:choose>
<xsl:when test="empty($context_id)">
<xsl:sequence select="xs:double(0)"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="F10" select="formula:F10
( $context_id)" as="xs:double"/>
<xsl:variable name="F10_prev" select="formula:F10
(helper:get_previous($context_id))" as="xs:double"/>
<xsl:variable name="F3" select="helper:get_input_value
( 'F3', $context_id)" as="xs:double"/>
<xsl:sequence select="if( ($F10 - $F10_prev) gt 0.01*$F10_prev )
then ($F10 + $F3) else ($F10)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--================HELPER FUNCTIONS================-->
<xsl:function name="helper:get_input_value" as="xs:double">
<xsl:param name="id" as="xs:string"/>
<xsl:param name="context_id" as="xs:string?"/>
<xsl:choose>
<xsl:when test="empty($context_id)">
<xsl:sequence select="xs:double(0)"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="val" select="$this_doc/key
('key_get_input_val', concat($context_id, '|', $id) )"/>
<xsl:sequence select="if (empty($val) or $val eq '') then xs:double(0) else xs:double($val)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--================================-->
<xsl:function name="helper:get_previous" as="xs:string?">
<xsl:param name="context_id" as="xs:string"/>
<xsl:variable name="context" select="$this_doc/key
('key_get_contextFromID', $context_id )"/>
<xsl:choose>
<xsl:when test="empty($context)">
<xsl:sequence select="()"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="endDate" select="$context/period/endDate" 
as="xs:date"/>
<xsl:variable name="adjustedDate" select="$endDate - 
xdt:yearMonthDuration('P1Y')"/>
<xsl:variable name="newNumericContext"
 select="$this_doc/key('key_get_contextFromEndDate', $adjustedDate )"/>
<xsl:sequence select="if( empty($newNumericContext) or 
$newNumericContext eq '') then () else $newNumericContext/@id"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</xsl:transform>


Listing 3: instance.xml

<?xml version="1.0" encoding="UTF-8"?><!-- 
=============================================
INSTANCE.XML - created by Ed Gimzewski, Fitch Ratings, 2005-01-12
=============================================
An instance of the psuedo schema SCHEMA.XML. Contexts are defined; only the endDate is
 used, but the duration is included to make the contexts more meaningful. Data is assigned
  to input items for a context.-->
<instance>
<!--ITEM DATA-->
<F1 context="period_1">100</F1>
<F1 context="period_2">101</F1>
<F1 context="period_3">102</F1>
<F2 context="period_1">200</F2>
<F2 context="period_2">201</F2>
<F2 context="period_3">202</F2>
<F3 context="period_1">300</F3>
<F3 context="period_2">301</F3>
<F3 context="period_3">302</F3>
<F4 context="period_1">400</F4>
<F4 context="period_2">401</F4>
<F4 context="period_3">402</F4>
<!--CONTEXTS-->
<context id="period_1">
<period>
<duration>P12M</duration>
<endDate>2001-12-31</endDate>
</period>
</context>
<context id="period_2">
<period>
<duration>P12M</duration>
<endDate>2002-12-31</endDate>
</period>
</context>
<context id="period_3">
<period>
<duration>P12M</duration>
<endDate>2003-12-31</endDate>
</period>
</context>
</instance>


Listing 4: compiler.xslt

<?xml version="1.0" encoding="UTF-8"?><xsl:transform version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:XSLOUT="anything"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
   xmlns:compiler="http://www.fitchratings.com/compiler"
    xmlns:formula="http://www.fitchratings.com/formula"
	 xmlns:helper="http://www.fitchratings.com/helper">
<!--
=============================================
COMPILER.XSLT - created by Ed Gimzewski, Fitch Ratings, 2005-01-12
=============================================
This code is a drastically simplfied version of the compiler used for our production work.
 It was used to convert:

SCHEMA.XML -> FORMULAS.XSLT

It is most easily understood with reference to the above files.

Note the use of namespaces:
'compiler': for names of functions used in this XSLT.
'XSLTOUT': to mark elements that will be output with namespace convertd to 'xsl', done by
 <xsl:namespace-alias>.
'formula': for names of functions in the created XSLT; these functions apply the formulas
'helper': for names of functions used in the created XSLT to help the 'formula:' functions
 there.

Note how nulls are represented:
as="xs:double" is a non-nullable double
as="xs:double?" is a nullable double if null is represented by the empty sequence.
-->
<xsl:output indent="yes" method="xml"/>
<xsl:namespace-alias stylesheet-prefix="XSLOUT" result-prefix="xsl"/>
<!--================CONSTANTS AND KEYS used by compiler================-->
<xsl:key name="key_get_calc_field" match="/schema/item" use="@id"/>
<xsl:variable name="this_doc" select="/schema"/>
<xsl:variable name="mod_prev" select="'prev'"/>
<xsl:variable name="mod_none" select="'none'"/>
<xsl:variable name="div" select="'div'"/>
<xsl:variable name="quote">
<xsl:text>'</xsl:text>
</xsl:variable>
<xsl:variable name="calc" select="'calc'"/>
<xsl:variable name="input" select="'input'"/>
<xsl:variable name="empty" select="'()'"/>
<!--================================-->
<xsl:template match="/">
<xsl:comment>
<xsl:text>
=====================================================
FUNCTIONS.XSLT

Created by transforming SCHEMA.XML with COMPILER.XSLT
=====================================================
</xsl:text>
</xsl:comment>
<XSLOUT:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xdt="http://www.w3.org/2003/11/xpath-datatypes">
<XSLOUT:output indent="yes" method="xml" encoding="UTF-8"/>
<xsl:comment>
<xsl:text>================CONSTANTS AND KEYS================</xsl:text>
</xsl:comment>
<XSLOUT:variable name="mod_prev" select="'prev'"/>
<XSLOUT:variable name="this_doc" select="/instance"/>
<XSLOUT:key name="key_get_input_val" match="/instance/*[name() ne 'context']"
 use="concat(@context, '|', name())"/>
<XSLOUT:key name="key_get_contextFromID" match="/instance/context" use="@id"/>
<XSLOUT:key name="key_get_contextFromEndDate" match="/instance/context"
 use="xs:date(period/endDate)"/>
<xsl:comment>
<xsl:text>================FORMULA FUNCTIONS================</xsl:text>
</xsl:comment>
<xsl:comment>
<xsl:text>================Simple formulas================</xsl:text>
</xsl:comment>
<xsl:for-each select="schema/item[@type eq $calc]">
<!-- get a sequence with every variable referenced in the function, including references
 to previous periods-->
<xsl:variable name="vars_formula" select="compiler:getSequenceString(@formula)"/>
<!--======write out a function======-->
<xsl:comment>=========================</xsl:comment>
<XSLOUT:function name="formula:{@id}" as="xs:double">
<XSLOUT:param name="context_id" as="xs:string?"/>
<XSLOUT:choose>
<XSLOUT:when test="empty($context_id)">
<XSLOUT:sequence select="xs:double(0)"/>
</XSLOUT:when>
<XSLOUT:otherwise>
<!-- Write out all the variables -->
<xsl:call-template name="compiler:write_out_variables">
<!-- use the complete list of arguments to write out each as a variable-->
<xsl:with-param name="vars_formula" select="$vars_formula"/>
</xsl:call-template>
<!-- Write out the final formula -->
<XSLOUT:sequence select="{@formula}"/>
</XSLOUT:otherwise>
</XSLOUT:choose>
</XSLOUT:function>
<!-- =========================-->
</xsl:for-each>
<xsl:comment>
<xsl:text>================HELPER FUNCTIONS================</xsl:text>
</xsl:comment>
<!-- The Helper functions are simply passed through to the output-->
<XSLOUT:function name="helper:get_input_value" as="xs:double">
<XSLOUT:param name="id" as="xs:string"/>
<XSLOUT:param name="context_id" as="xs:string?"/>
<XSLOUT:choose>
<XSLOUT:when test="empty($context_id)">
<XSLOUT:sequence select="xs:double(0)"/>
</XSLOUT:when>
<XSLOUT:otherwise>
<XSLOUT:variable name="val" select="$this_doc/key('key_get_input_val',
 concat($context_id, '|', $id) )"/>
<XSLOUT:sequence select="if (empty($val) or $val eq '') then xs:double(0) else
 xs:double($val)"/>
</XSLOUT:otherwise>
</XSLOUT:choose>
</XSLOUT:function>
<xsl:comment>================================</xsl:comment>
<XSLOUT:function name="helper:get_previous" as="xs:string?">
<XSLOUT:param name="context_id" as="xs:string"/>
<XSLOUT:variable name="context" select="$this_doc/key('key_get_contextFromID',
 $context_id )"/>
<XSLOUT:choose>
<XSLOUT:when test="empty($context)">
<XSLOUT:sequence select="()"/>
</XSLOUT:when>
<XSLOUT:otherwise>
<XSLOUT:variable name="endDate" select="$context/period/endDate" as="xs:date"/>
<XSLOUT:variable name="adjustedDate" select="$endDate - xdt:yearMonthDuration('P1Y')"/>
<XSLOUT:variable name="newNumericContext"
 select="$this_doc/key('key_get_contextFromEndDate', $adjustedDate )"/>
<XSLOUT:sequence select="if( empty($newNumericContext) or 
$newNumericContext eq '') then () else $newNumericContext/@id"/>
</XSLOUT:otherwise>
</XSLOUT:choose>
</XSLOUT:function>
</XSLOUT:transform>
</xsl:template>
<!--===================COMPILER FUNCTIONS======================-->
<!-- These are used in the compilation process-->
<xsl:function name="compiler:is_an_input_var" as="xs:boolean">
<xsl:param name="var_name"/>
<xsl:variable name="var_name_without_prev" select="if (contains($var_name,'_')) then
 substring-before($var_name,'_') else $var_name "/>
<xsl:sequence select=" if (($this_doc/key('key_get_calc_field',
 $var_name_without_prev)/@type) eq $input) then true() else false()"/>
</xsl:function>
<!--================================================================-->
<xsl:function name="compiler:get_field_ref_modifer">
<xsl:param name="var_name"/>
<!-- e.g. $F12345_prev return prev-->
<xsl:sequence select=" if (contains($var_name,concat('_',$mod_prev))) 
then $mod_prev else $mod_none"/>
</xsl:function>
<!--================================================================-->
<xsl:function name="compiler:get_field_ref_without_modifer">
<xsl:param name="var_name"/>
<!-- e.g. $F12345_prev then return F12345 -->
<xsl:sequence select=" if (contains($var_name,'_')) then
 substring-after(substring-before($var_name,'_'),'$') else substring-after($var_name,'$')
 "/>
</xsl:function>
<!--================================================================-->
<xsl:template name="compiler:write_out_variables">
<xsl:param name="vars_formula"/>
<!--writes out each function argument as a variable
e.g. for sequence "$F1, $F2, $F1_prev" it writes out the three variable declarations-->
<xsl:if test="$vars_formula ne $empty">
<xsl:for-each select="tokenize($vars_formula,',')">
<xsl:variable name="id" select="substring-after(.,'$')"/>
<xsl:variable name="mod" select="compiler:get_field_ref_modifer(.)"/>
<!--e.g. get prev from $F1_prev -->
<xsl:variable name="field_ref_without_modifer"
 select="compiler:get_field_ref_without_modifer(.)"/>
<!--e.g. get F1 from $F1_prev -->
<xsl:choose>
<xsl:when test="compiler:is_an_input_var($field_ref_without_modifer)">
<!-- is an input variable-->
<xsl:choose>
<xsl:when test="$mod eq $mod_none">
<!-- has no modifer-->
<XSLOUT:variable name="{$id}" select="helper:get_input_value( {concat($quote,
 $field_ref_without_modifer,$quote)}, $context_id)" as="xs:double"/>
</xsl:when>
<xsl:otherwise>
<!-- has a modifer-->
<XSLOUT:variable name="{$id}" select="helper:get_input_value(
 {concat($quote,$field_ref_without_modifer,$quote)}, helper:get_previous($context_id))"
  as="xs:double"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<!-- is a calculated variable-->
<xsl:choose>
<xsl:when test="$mod eq $mod_none">
<!-- has no modifer-->
<XSLOUT:variable name="{$id}" select="formula:{$field_ref_without_modifer}(
 $context_id)" as="xs:double"/>
</xsl:when>
<xsl:otherwise>
<!-- has a modifer-->
<XSLOUT:variable name="{$id}"
 select="formula:{$field_ref_without_modifer}(helper:get_previous($context_id))"
  as="xs:double"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:if>
</xsl:template>
<!--- ====================================-->
<xsl:function name="compiler:getSequenceString">
<xsl:param name="formula"/>
<!-- get a unique, space delimited list of variable names like
$F12345 or $F12345_prev from a formula-->
<xsl:variable name="res1">
<xsl:analyze-string select="$formula" regex="(\$F([0-9]+)(_prev)?)+">
<xsl:matching-substring>
<xsl:value-of select="concat( regex-group(1) , ' ' )"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($res1,'$')">
<!-- the list is not empty, so tokenize, get distinct item names, and
write out as comma-separated values-->
<xsl:sequence select="string-join(distinct-values(tokenize(normalize-space($res1),'\s')),',')"/>
</xsl:when>
<xsl:otherwise>
<!-- there are no variables -->
<xsl:sequence select="$empty"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--- ====================================-->
</xsl:transform>


Listing 5: host.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:formula="http://www.fitchratings.com/formula" version="2.0"
   xmlns:xdt="http://www.w3.org/2003/11/xpath-datatypes">
<!--
=============================================
HOST.XSLT - created by Ed Gimzewski, Fitch Ratings, 2005-01-12
=============================================
This shows how the formulas in FORMULAS.XSLT are referenced (<xsl:include>) and called.
-->
<xsl:output indent="yes" method="xml" encoding="UTF-8"/>
<xsl:include href="functions.xslt"/>
<!--================Calling formula functions=============-->
<xsl:template match="/">
<calculated_data>
<F10 context="period_1">
<xsl:sequence select="formula:F10('period_1')"/>
</F10>
<F10 context="period_2">
<xsl:sequence select="formula:F10('period_2')"/>
</F10>
<F10 context="period_3">
<xsl:sequence select="formula:F10('period_3')"/>
</F10>
<F11 context="period_1">
<xsl:sequence select="formula:F11('period_1')"/>
</F11>
<F11 context="period_2">
<xsl:sequence select="formula:F11('period_2')"/>
</F11>
<F11 context="period_3">
<xsl:sequence select="formula:F11('period_3')"/>
</F11>
<F12 context="period_1">
<xsl:sequence select="formula:F12('period_1')"/>
</F12>
<F12 context="period_2">
<xsl:sequence select="formula:F12('period_2')"/>
</F12>
<F12 context="period_3">
<xsl:sequence select="formula:F12('period_3')"/>
</F12>
<F13 context="period_1">
<xsl:sequence select="formula:F13('period_1')"/>
</F13>
<F13 context="period_2">
<xsl:sequence select="formula:F13('period_2')"/>
</F13>
<F13 context="period_3">
<xsl:sequence select="formula:F13('period_3')"/>
</F13>
</calculated_data>
</xsl:template>
</xsl:transform>


Listing 6: calculated_data.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--
=============================================
CALCULATED_DATA.XML - annotated by Ed Gimzewski, Fitch Ratings, 2005-01-12
=============================================
Created by transforming INSTANCE.XML using HOST.XSLT (the latter <xsl:include>-s
 FUNCTIONS.XSLT -->

<calculated_data xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:formula="http://www.fitchratings.com/formula"
  xmlns:xdt="http://www.w3.org/2003/11/xpath-datatypes">
<F10 context="period_1">300</F10>
<F10 context="period_2">302</F10>
<F10 context="period_3">304</F10>
<F11 context="period_1">3</F11>
<F11 context="period_2">3</F11>
<F11 context="period_3">3</F11>
<F12 context="period_1">1.5</F12>
<F12 context="period_2">0.009950248756218905</F12>
<F12 context="period_3">0.009900990099009901</F12>
<F13 context="period_1">600</F13>
<F13 context="period_2">302</F13>
<F13 context="period_3">304</F13>
</calculated_data>


Listing 7: schema_complex.xml

<?xml version="1.0" encoding="UTF-8"?><!--
=============================================
SCHEMA_COMPLEX.XML - created by Ed Gimzewski, Fitch Ratings, 2005-01-12
=============================================
A pseudo schema that has the basic information that XBRL would hold in: an XSchema file
 plus a formula linkbase. An additional attribute indicates the null-related logic to be
  applied when the formulas are evaluated.--><schema>
<!-- items for input data-->
<item id="F1" type="input"/>
<item id="F2" type="input"/>
<item id="F3" type="input"/>
<item id="F4" type="input"/>
<!-- items for calculated data.-->
<item id="F20" type="simple_calc" formula="$F1 + $F2"
 null_eval_logic="$null_if_all_null"/>
<item id="F21" type="simple_calc" formula="$F20 - $F3"
 null_eval_logic="$null_if_any_null"/>
<item id="F22" type="simple_calc" formula="$F20 - $F20_prev + $F4"
 null_eval_logic="$null_if_all_null"/>
<item id="F23" type="ratio_calc" numerator="$F1 - $F2" denominator="$F3 - $F4"
 null_eval_logic="$null_if_all_null"/>
<item id="F24" type="conditional_calc" test_left_hand="$F1 + $F2" test_operator="ne"
 test_right_hand="$F3 + $F4" formula_if_true="$F1 - $F2" formula_if_false="$F3 - $F4"
  null_eval_logic="$null_if_all_null"/>
</schema>


Listing 8: functions_complex.xslt

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:xdt="http://www.w3.org/2003/11/xpath-datatypes"
   xmlns:compiler="http://www.fitchratings.com/compiler"
    xmlns:formula="http://www.fitchratings.com/formula"
	 xmlns:helper="http://www.fitchratings.com/helper" version="2.0">
<xsl:output indent="yes" method="xml" encoding="UTF-8"/>
<!--

=============================================
FUNCTIONS_COMPLEX.XSLT - created by Ed Gimzewski, Fitch Ratings, 2005-01-12
=============================================
It differs from the simpler FUNCTIONS.XSLT in that it applies the business rules for
 handling null data. Note how nulls are represented:
as="xs:double" is a non-nullable double
as="xs:double?" is a nullable double if null is represented by the empty sequence.

The nullable variables are named as $nF... and are used to work out the null status of the
 formula; the non-nullable equivalents are named as $F.. and are used in the formula. The
  nullable variables can't be used in the formula since XSLT does not treat an empty
   variable as zero; i.e. in $nF1 + $nF2, if $nF1 is empty, the result is empty.

It should also be noted that while a formula function here simply returns a "xs:double?" it
 could return an element that could contain the value plus other information. For example,
  if an item is calculated by subtracting two point-in-time values, we may want the result
   to carry the duration as that may be required by another calculation that uses this
    value. In that case the formula could return a element like:

<item-value>
<value>999</value>
<is_null>no</is_null>
<duration>P6M</duration>
</item-value>

This can be incorporated into the code here relatively easily. The variables for
 numerically evaluating the formula would use the value element; other logic would use the
  other parts.
-->
<!--================CONSTANTS AND KEYS================-->
<xsl:variable name="mod_prev" select="'prev'"/>
<xsl:variable name="this_doc" select="/instance"/>
<xsl:variable name="null_if_any_null" select="'null_if_any_null'" as="xs:string"/>
<xsl:variable name="null_if_all_null" select="'null_if_all_null'" as="xs:string"/>
<xsl:key name="key_get_input_val" match="/instance/*[name() ne 'context']"
 use="concat(@context, '|', name())"/>
<xsl:key name="key_get_contextFromID" match="/instance/context" use="@id"/>
<xsl:key name="key_get_contextFromEndDate" match="/instance/context"
 use="xs:date(period/endDate)"/>
<!--================FORMULA FUNCTIONS================-->
<!--================Simple formulas================-->
<!--=====================================================-->
<xsl:function name="formula:F20" as="xs:double?">
<!--Formula:$F1 + $F2-->
<xsl:param name="context_id" as="xs:string?"/>
<xsl:variable name="null_eval_logic" select="$null_if_all_null" as="xs:string"/>
<xsl:choose>
<xsl:when test="empty($context_id)">
<xsl:sequence select="()"/>
</xsl:when>
<xsl:otherwise>
<!--Get the nullable variables (nF...) for null evaluations and the number-part-only
variables (F...) for evaluating the formula.-->
<xsl:variable name="nF1" select="helper:get_input_value( 'F1', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F1" select="helper:null_to_zero($nF1)" as="xs:double"/>
<xsl:variable name="nF2" select="helper:get_input_value( 'F2', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F2" select="helper:null_to_zero($nF2)" as="xs:double"/>
<!--Get the sequences needed for null evaluation-->
<xsl:variable name="vars_formula" select="empty($nF1),empty($nF2)" as="xs:boolean*"/>
<xsl:variable name="vars_formula_prev_p" select="()" as="xs:boolean*"/>
<!--Get the null status-->
<xsl:variable name="is_null" select="helper:get_null_for_simple_type($vars_formula,
 $vars_formula_prev_p, $null_eval_logic)" as="xs:boolean"/>
<!--Evaluate the formula-->
<xsl:sequence select="if($is_null) then () else ($F1 + $F2)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--=====================================================-->
<xsl:function name="formula:F21" as="xs:double?">
<!--Formula:$F20 - $F3-->
<xsl:param name="context_id" as="xs:string?"/>
<xsl:variable name="null_eval_logic" select="$null_if_any_null" as="xs:string"/>
<xsl:choose>
<xsl:when test="empty($context_id)">
<xsl:sequence select="()"/>
</xsl:when>
<xsl:otherwise>
<!--Get the nullable variables (nF...) for null evaluations and the number-part-only
variables (F...) for evaluating the formula.-->
<xsl:variable name="nF20" select="formula:F20( $context_id)" as="xs:double?"/>
<xsl:variable name="F20" select="helper:null_to_zero($nF20)" as="xs:double"/>
<xsl:variable name="nF3" select="helper:get_input_value( 'F3', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F3" select="helper:null_to_zero($nF3)" as="xs:double"/>
<!--Get the sequences needed for null evaluation-->
<xsl:variable name="vars_formula" select="empty($nF20),empty($nF3)" as="xs:boolean*"/>
<xsl:variable name="vars_formula_prev_p" select="()" as="xs:boolean*"/>
<!--Get the null status-->
<xsl:variable name="is_null" select="helper:get_null_for_simple_type($vars_formula,
 $vars_formula_prev_p, $null_eval_logic)" as="xs:boolean"/>
<!--Evaluate the formula-->
<xsl:sequence select="if($is_null) then () else ($F20 - $F3)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--=====================================================-->
<xsl:function name="formula:F22" as="xs:double?">
<!--Formula:$F20 - $F20_prev + $F4-->
<xsl:param name="context_id" as="xs:string?"/>
<xsl:variable name="null_eval_logic" select="$null_if_all_null" as="xs:string"/>
<xsl:choose>
<xsl:when test="empty($context_id)">
<xsl:sequence select="()"/>
</xsl:when>
<xsl:otherwise>
<!--Get the nullable variables (nF...) for null evaluations and the number-part-only
variables (F...) for evaluating the formula.-->
<xsl:variable name="nF20" select="formula:F20( $context_id)" as="xs:double?"/>
<xsl:variable name="F20" select="helper:null_to_zero($nF20)" as="xs:double"/>
<xsl:variable name="nF20_prev" select="formula:F20(helper:get_previous($context_id))"
 as="xs:double?"/>
<xsl:variable name="F20_prev" select="helper:null_to_zero($nF20_prev)" as="xs:double"/>
<xsl:variable name="nF4" select="helper:get_input_value( 'F4', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F4" select="helper:null_to_zero($nF4)" as="xs:double"/>
<!--Get the sequences needed for null evaluation-->
<xsl:variable name="vars_formula" select="empty($nF20),empty($nF20_prev),empty($nF4)"
 as="xs:boolean*"/>
<xsl:variable name="vars_formula_prev_p" select="empty($nF20_prev)" as="xs:boolean*"/>
<!--Get the null status-->
<xsl:variable name="is_null" select="helper:get_null_for_simple_type($vars_formula,
 $vars_formula_prev_p, $null_eval_logic)" as="xs:boolean"/>
<!--Evaluate the formula-->
<xsl:sequence select="if($is_null) then () else ($F20 - $F20_prev + $F4)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--================Ratio formulas================-->
<!--=====================================================-->
<xsl:function name="formula:F23" as="xs:double?">
<!--Formula:($F1 - $F2) div ($F3 - $F4)-->
<xsl:param name="context_id" as="xs:string?"/>
<xsl:variable name="null_eval_logic" select="$null_if_all_null" as="xs:string"/>
<xsl:choose>
<xsl:when test="empty($context_id)">
<xsl:sequence select="()"/>
</xsl:when>
<xsl:otherwise>
<!--Get the nullable variables (nF...) for null evaluations and the number-part-only
variables (F...) for evaluating the formula.-->
<xsl:variable name="nF1" select="helper:get_input_value( 'F1', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F1" select="helper:null_to_zero($nF1)" as="xs:double"/>
<xsl:variable name="nF2" select="helper:get_input_value( 'F2', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F2" select="helper:null_to_zero($nF2)" as="xs:double"/>
<xsl:variable name="nF3" select="helper:get_input_value( 'F3', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F3" select="helper:null_to_zero($nF3)" as="xs:double"/>
<xsl:variable name="nF4" select="helper:get_input_value( 'F4', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F4" select="helper:null_to_zero($nF4)" as="xs:double"/>
<!--Get the sequences and values needed for null evaluation-->
<xsl:variable name="vars_numerator" select="empty($nF1),empty($nF2)" as="xs:boolean*"/>
<xsl:variable name="vars_denominator" select="empty($nF3),empty($nF4)"
 as="xs:boolean*"/>
<xsl:variable name="vars_numerator_prev_p" select="()" as="xs:boolean*"/>
<xsl:variable name="vars_denominator_prev_p" select="()" as="xs:boolean*"/>
<!--Get the null status-->
<xsl:variable name="is_null" select="helper:get_null_for_ratio_type($vars_numerator,
 $vars_numerator_prev_p,$vars_denominator,$vars_denominator_prev_p, $null_eval_logic)"
  as="xs:boolean"/>
<!--Evaluate the formula-->
<xsl:variable name="numerator" select="$F1 - $F2" as="xs:double"/>
<xsl:variable name="denominator" select="$F3 - $F4" as="xs:double"/>
<xsl:sequence select="if($is_null) then () else ($numerator div $denominator)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--================Conditional formulas================-->
<!--=====================================================-->
<xsl:function name="formula:F24" as="xs:double?">
<!--Formula:if (($F1 + $F2) ne ($F3 + $F4)) then ($F1 - $F2) else ($F3 - $F4)-->
<xsl:param name="context_id" as="xs:string?"/>
<xsl:variable name="null_eval_logic" select="$null_if_all_null" as="xs:string"/>
<xsl:choose>
<xsl:when test="empty($context_id)">
<xsl:sequence select="()"/>
</xsl:when>
<xsl:otherwise>
<!--Get the nullable variables (nF...) for null evaluations and the number-part-only
variables (F...) for evaluating the formula.-->
<xsl:variable name="nF1" select="helper:get_input_value( 'F1', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F1" select="helper:null_to_zero($nF1)" as="xs:double"/>
<xsl:variable name="nF2" select="helper:get_input_value( 'F2', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F2" select="helper:null_to_zero($nF2)" as="xs:double"/>
<xsl:variable name="nF3" select="helper:get_input_value( 'F3', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F3" select="helper:null_to_zero($nF3)" as="xs:double"/>
<xsl:variable name="nF4" select="helper:get_input_value( 'F4', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F4" select="helper:null_to_zero($nF4)" as="xs:double"/>
<!--Get the sequences needed for null evaluation of test_left_hand and
 test_right_hand-->
<xsl:variable name="vars_test_left_hand" select="empty($nF1),empty($nF2)"
 as="xs:boolean*"/>
<xsl:variable name="vars_test_right_hand" select="empty($nF3),empty($nF4)"
 as="xs:boolean*"/>
<xsl:variable name="vars_test_left_hand_prev_p" select="()" as="xs:boolean*"/>
<xsl:variable name="vars_test_right_hand_prev_p" select="()" as="xs:boolean*"/>
<!--Get null status of test_left_hand and test_right_hand-->
<xsl:variable name="is_null_left_hand"
 select="helper:get_null_for_simple_type($vars_test_left_hand, $vars_test_left_hand_prev_p,
  $null_eval_logic)"/>
<xsl:variable name="is_null_right_hand"
 select="helper:get_null_for_simple_type($vars_test_right_hand,
  $vars_test_right_hand_prev_p, $null_eval_logic)"/>
<!--Find out how to evaluate the test-->
<xsl:variable name="test_should_use_values"
 select="helper:test_should_use_values($is_null_left_hand, $is_null_right_hand, 'ne')"/>
<!--Evaluate the test-->
<xsl:variable name="test_is_true" as="xs:boolean">
<xsl:choose>
<xsl:when test="$test_should_use_values">
<!--Evaluate using values of test_left_hand and test_right_hand-->
<xsl:variable name="left_hand_value" select="if ($is_null_left_hand) then xs:double(0
 else ($F1 + $F2)" as="xs:double"/>
<xsl:variable name="right_hand_vlue" select="if ($is_null_right_hand) then xs:double(0)
 else ($F3 + $F4)" as="xs:double"/>
<xsl:sequence select="$left_hand_value ne $right_hand_value"/>
</xsl:when>
<xsl:otherwise>
<!--Evaluate using null status of test_left_hand and test_right_hand-->
<xsl:sequence select="$is_null_left_hand ne $is_null_right_hand"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$test_is_true">
<!--Evaluate formula_if_true as if a simple type-->
<!--Get the nullable variables (nF...) for null evaluations and the number-part-only
 variables (F...) for evaluating the formula.-->
<xsl:variable name="nF1" select="helper:get_input_value( 'F1', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F1" select="helper:null_to_zero($nF1)" as="xs:double"/>
<xsl:variable name="nF2" select="helper:get_input_value( 'F2',
 $context_id)"as="xs:double?"/>
<xsl:variable name="F2" select="helper:null_to_zero($nF2)" as="xs:double"/>
<xsl:variable name="vars_formula" select="empty($nF1),empty($nF2)" as="xs:boolean*"/>
<xsl:variable name="vars_formula_prev_p" select="()" as="xs:boolean*"/>
<xsl:variable name="is_null" select="helper:get_null_for_simple_type($vars_formula,
 $vars_formula_prev_p, $null_eval_logic)" as="xs:boolean"/>
<xsl:sequence select="if($is_null) then () else ($F1 - $F2)"/>
</xsl:when>
<xsl:otherwise>
<!--Evaluate formula_if_false as if a simple type-->
<!--Get the nullable variables (nF...) for null evaluations and the number-part-only
variables (F...) for evaluating the formula.-->
<xsl:variable name="nF3" select="helper:get_input_value( 'F3', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F3" select="helper:null_to_zero($nF3)" as="xs:double"/>
<xsl:variable name="nF4" select="helper:get_input_value( 'F4', $context_id)"
 as="xs:double?"/>
<xsl:variable name="F4" select="helper:null_to_zero($nF4)" as="xs:double"/>
<xsl:variable name="vars_formula" select="empty($nF3),empty($nF4)" as="xs:boolean*"/>
<xsl:variable name="vars_formula_prev_p" select="()" as="xs:boolean*"/>
<xsl:variable name="is_null" select="helper:get_null_for_simple_type($vars_formula,
 $vars_formula_prev_p, $null_eval_logic)" as="xs:boolean"/>
<xsl:sequence select="if($is_null) then () else ($F3 - $F4)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--================HELPER FUNCTIONS================-->
<xsl:function name="helper:get_input_value" as="xs:double?">
<xsl:param name="id" as="xs:string"/>
<xsl:param name="context_id" as="xs:string?"/>
<xsl:choose>
<xsl:when test="empty($context_id)">
<xsl:sequence select="()"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="val" select="$this_doc/key('key_get_input_val', concat($context_id,
 '|', $id) )"/>
<xsl:sequence select="if (empty($val) or $val eq '') then () else xs:double($val)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--================================-->
<xsl:function name="helper:get_previous" as="xs:string?">
<xsl:param name="context_id" as="xs:string"/>
<xsl:variable name="context" select="$this_doc/key('key_get_contextFromID', $context_id
 )"/>
<xsl:choose>
<xsl:when test="empty($context)">
<xsl:sequence select="()"/>
</xsl:when>
<xsl:otherwise>
<!--Look back one year-->
<xsl:variable name="endDate" select="$context/period/endDate" as="xs:date"/>
<xsl:variable name="adjustedDate" select="$endDate - xdt:yearMonthDuration('P1Y')"/>
<xsl:variable name="newNumericContext"
 select="$this_doc/key('key_get_contextFromEndDate', $adjustedDate )"/>
<xsl:sequence select="if( empty($newNumericContext) or $newNumericContext eq '') then ()
 else $newNumericContext/@id"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--================================-->
<xsl:function name="helper:get_null_for_simple_type" as="xs:boolean">
<xsl:param name="vars" as="xs:boolean*"/>
<xsl:param name="vars_prev_p" as="xs:boolean*"/>
<xsl:param name="null_eval_logic" as="xs:string"/>
<xsl:sequence select="if (some $x in $vars_prev_p satisfies $x eq true()) then true()
 else if ($null_eval_logic eq $null_if_all_null) then if(every $x in $vars satisfies $x eq
  true()) then true() else false() else if ($null_eval_logic eq $null_if_any_null) then
   if(some $x in $vars satisfies $x eq true()) then true() else false() else false()"/>
</xsl:function>
<!--================================-->
<xsl:function name="helper:get_null_for_ratio_type" as="xs:boolean">
<xsl:param name="vars_numerator" as="xs:boolean*"/>
<xsl:param name="vars_numerator_prev_p" as="xs:boolean*"/>
<xsl:param name="vars_denominator" as="xs:boolean*"/>
<xsl:param name="vars_denominator_prev_p" as="xs:boolean*"/>
<xsl:param name="null_eval_logic" as="xs:string"/>
<!--Get null status of numerator and denominator as if they are simple types-->
<xsl:variable name="numerator_is_null"
 select="helper:get_null_for_simple_type($vars_numerator,
  $vars_numerator_prev_p,$null_eval_logic)" as="xs:boolean"/>
<xsl:variable name="denominator_is_null"
 select="helper:get_null_for_simple_type($vars_denominator,$vars_denominator_prev_p,
  $null_eval_logic)" as="xs:boolean"/>
<!--if either is null, then return null-->
<xsl:sequence select="if ($numerator_is_null or $denominator_is_null) then true() else
 false()"/>
</xsl:function>
<!--================================-->
<xsl:function name="helper:test_should_use_values" as="xs:boolean">
<xsl:param name="is_null_left_hand" as="xs:boolean"/>
<xsl:param name="is_null_right_hand" as="xs:boolean"/>
<xsl:param name="op" as="xs:string"/>
<xsl:choose>
<xsl:when test="$op eq 'gt' or $op eq 'lt'">
<xsl:sequence select="true()"/>
</xsl:when>
<xsl:otherwise>
<!--Operator is eq/ne and null statuses are different-->
<xsl:sequence select="if( $is_null_left_hand ne $is_null_right_hand) then false() else
 true()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!--================================-->
<xsl:function name="helper:has_null_reference_to_previous_p" as="xs:boolean">
<xsl:param name="vars_formula_prev_p" as="xs:boolean*"/>
<xsl:sequence select="if (some $x in $vars_formula_prev_p satisfies $x eq true()) then
 true() else false()"/>
<!--================================-->
</xsl:function>
<xsl:function name="helper:null_to_zero" as="xs:double">
<xsl:param name="val" as="xs:double?"/>
<xsl:sequence select="if (empty($val)) then xs:double(0) else $val"/>
</xsl:function>
</xsl:transform>