403 lines
14 KiB
XML
403 lines
14 KiB
XML
<?xml version="1.0" encoding="ISO-8859-1"?>
|
|
<!--
|
|
Copyright (C) 2014-2020 Ryan Specialty Group, LLC.
|
|
|
|
This file is part of tame-core.
|
|
|
|
tame-core is free software: you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
-->
|
|
<package xmlns="http://www.lovullo.com/rater"
|
|
xmlns:c="http://www.lovullo.com/calc"
|
|
xmlns:t="http://www.lovullo.com/rater/apply-template"
|
|
core="true"
|
|
desc="Interpolation">
|
|
|
|
<import package="../base" />
|
|
<import package="../numeric/common" />
|
|
|
|
|
|
<template name="_interpolate_" desc="Interpolate the first two values in a vector">
|
|
<param name="@values@" desc="Set" />
|
|
<param name="@low@" desc="Lower value" />
|
|
<param name="@high@" desc="Upper value" />
|
|
<param name="@actual@" desc="Actual value" />
|
|
|
|
|
|
<c:let>
|
|
<c:values>
|
|
<!-- allows us to reference the set values -->
|
|
<c:value name="orig_set" type="float" set="vector" desc="The given set">
|
|
<param-copy name="@values@" />
|
|
</c:value>
|
|
</c:values>
|
|
|
|
<c:let>
|
|
<c:values>
|
|
<!-- ensure that the set is ordered such that the lower value is the first index -->
|
|
<c:value name="set" type="float" set="vector" desc="Ordered set">
|
|
<c:let>
|
|
<c:values>
|
|
<c:value name="a" type="float" desc="First set value">
|
|
<c:car>
|
|
<c:value-of name="orig_set" />
|
|
</c:car>
|
|
</c:value>
|
|
|
|
<c:value name="b" type="float" desc="Second set value">
|
|
<c:value-of name="orig_set">
|
|
<c:index>
|
|
<c:const value="1" type="integer" desc="Second index" />
|
|
</c:index>
|
|
</c:value-of>
|
|
</c:value>
|
|
</c:values>
|
|
|
|
<c:cases>
|
|
<!-- when a > b, reorder -->
|
|
<c:case>
|
|
<c:when name="a">
|
|
<c:gt>
|
|
<c:value-of name="b" />
|
|
</c:gt>
|
|
</c:when>
|
|
|
|
<c:vector label="Re-ordered set such that the lower value is first in the vector">
|
|
<c:value-of name="b" />
|
|
<c:value-of name="a" />
|
|
</c:vector>
|
|
</c:case>
|
|
|
|
<!-- already ordered -->
|
|
<c:otherwise>
|
|
<c:value-of name="orig_set" />
|
|
</c:otherwise>
|
|
</c:cases>
|
|
</c:let>
|
|
</c:value>
|
|
|
|
<!-- determine the skip to use for the vecstep call -->
|
|
<c:value name="skip" type="float" desc="First value to be used as skip">
|
|
<c:value-of name="@low@" />
|
|
</c:value>
|
|
|
|
<!-- determine the step to use for the vecstep call -->
|
|
<c:value name="step" type="float" desc="Use difference between the first two as the step">
|
|
<c:sum label="Step between the low and high values">
|
|
<c:value-of name="@high@" />
|
|
|
|
<t:negate>
|
|
<c:value-of name="@low@" />
|
|
</t:negate>
|
|
</c:sum>
|
|
</c:value>
|
|
</c:values>
|
|
|
|
<c:cases>
|
|
<!-- check to see if interpolation is even necessary; in particular,
|
|
this will prevent a step of 0 to vecstep, which would eventually
|
|
result in a division by 0 -->
|
|
<c:case>
|
|
<c:when name="step">
|
|
<c:eq>
|
|
<c:const value="0" type="integer" desc="No step indicates identical values" />
|
|
</c:eq>
|
|
</c:when>
|
|
|
|
<!-- just return the first value; it's exact and no interpolation is necessary -->
|
|
<c:value-of name="set">
|
|
<c:index>
|
|
<c:const value="0" type="integer" desc="First index" />
|
|
</c:index>
|
|
</c:value-of>
|
|
</c:case>
|
|
|
|
<!-- values are inexact; interpolation is required -->
|
|
<c:otherwise>
|
|
<!-- give the values computed above, we can re-use vecstep on the first two values on the vector -->
|
|
<c:apply name="vecstep" set="set" skip="skip" step="step" value="@actual@" />
|
|
</c:otherwise>
|
|
</c:cases>
|
|
</c:let>
|
|
</c:let>
|
|
</template>
|
|
|
|
|
|
<!--
|
|
Perform interpolation on the results of a table query
|
|
|
|
Not only is interpolation itself obnoxious, but determining the values
|
|
to look up from a table in order to get the data *to* interpolate also
|
|
results in a great deal of boilerplate code. This makes you not want to
|
|
kill yourself. At least not because of this interpolation query
|
|
-->
|
|
<template name="_interpolate-query-field_"
|
|
desc="Interpolate table data">
|
|
<param name="@table@" desc="Table to query" />
|
|
<param name="@field@" desc="Table field to query" />
|
|
<param name="@key@" desc="Predicate subject column" />
|
|
<param name="@step@" desc="Key step" />
|
|
<param name="@values@" desc="Query predicates" />
|
|
|
|
<param name="@actual@"
|
|
desc="Actual value" />
|
|
|
|
<param name="@index@" desc="Actual value index">
|
|
<text></text>
|
|
</param>
|
|
|
|
<param name="@table_max@"
|
|
desc="Maximum value in table (should be a multiple of @step)" />
|
|
|
|
<!-- TODO: accept a function to calculate factor -->
|
|
<param name="@step_factor@"
|
|
desc="Factor to use per step after maximum to infer value" />
|
|
<param name="@step_factor_index@"
|
|
desc="Index of step factor value">
|
|
<text></text>
|
|
</param>
|
|
|
|
|
|
<c:let>
|
|
<c:values>
|
|
<!-- if the requested limit exceeds the maximum value we support in
|
|
the table, then look up the maximum -->
|
|
<c:value name="lookup" type="float"
|
|
desc="Value to retrieve and interpolate">
|
|
<!-- maximum provided -->
|
|
<if name="@table_max@">
|
|
<t:cap name="@table_max@">
|
|
<c:value-of name="@actual@" />
|
|
</t:cap>
|
|
</if>
|
|
|
|
<!-- no maximum -->
|
|
<unless name="@table_max@">
|
|
<c:value-of name="@actual@" index="@index@" />
|
|
</unless>
|
|
</c:value>
|
|
</c:values>
|
|
|
|
|
|
<c:sum>
|
|
<!-- lookup -->
|
|
<t:let-round name="lookup"
|
|
step="@step@"
|
|
high="high"
|
|
low="low">
|
|
<!-- query and interpolate -->
|
|
<t:interpolate low="low"
|
|
high="high"
|
|
actual="lookup">
|
|
<t:query-field table="@table@"
|
|
field="@field@">
|
|
<!-- query for upper and lower values for interpolation -->
|
|
<t:where-eq field="@key@">
|
|
<c:value-of name="low" />
|
|
<c:value-of name="high" />
|
|
</t:where-eq>
|
|
|
|
<param-copy name="@values@" />
|
|
</t:query-field>
|
|
</t:interpolate>
|
|
</t:let-round>
|
|
|
|
<!-- step factor -->
|
|
<if name="@step_factor@">
|
|
<c:let>
|
|
<c:values>
|
|
<!-- additional value (of key) that needs to be infered -->
|
|
<c:value name="add" type="float"
|
|
desc="Additional value">
|
|
<c:sum>
|
|
<c:value-of name="@actual@" index="@index@" />
|
|
<t:negate>
|
|
<c:value-of name="lookup" />
|
|
</t:negate>
|
|
</c:sum>
|
|
</c:value>
|
|
</c:values>
|
|
|
|
|
|
<c:product>
|
|
<c:value-of name="@step_factor@"
|
|
index="@step_factor_index@" />
|
|
<c:quotient>
|
|
<c:value-of name="add" />
|
|
<c:value-of name="@step@" />
|
|
</c:quotient>
|
|
</c:product>
|
|
</c:let>
|
|
</if>
|
|
</c:sum>
|
|
</c:let>
|
|
</template>
|
|
|
|
|
|
<!--
|
|
Calculates a floating-point representation of an arbitrary position within a
|
|
vector of values, where the value may exist between two elements within the
|
|
vector.
|
|
|
|
For example, given the following vector with the value of 12.5:
|
|
|
|
[ 5 ]
|
|
[ 10 ]
|
|
<- value is somewhere in here
|
|
[ 15 ]
|
|
[ ... ]
|
|
[ 100 ]
|
|
|
|
(Since the vector is 0-indexed, and we know that the index of 10 and 15 are
|
|
1 and 2 respectively, a value halfway between them would have an index of
|
|
exactly 1.5; so that's the value we're looking for.)
|
|
|
|
To calculate its position, given a 0-indexed vector, we need to know (a) the
|
|
step between each value and (b) the offset of the first step (the skip). In
|
|
the above case, index 0 represents 5, so let's assume that the skip provided
|
|
to us is 5. In this case, we have:
|
|
|
|
12.5 - 5 = 7.5
|
|
|
|
Which is the value without the skip. We can now simply divide it by the
|
|
step:
|
|
|
|
7.5 / 5 = 1.5, Q.E.D.
|
|
-->
|
|
<function name="vecpos" desc="Calculate the position of a value within a vector of a given step">
|
|
<param name="step" type="float" desc="Step between each of the values" />
|
|
<param name="skip" type="float" desc="Amount skipped before first element of vector" />
|
|
<param name="value" type="float" desc="Arbitrary value" />
|
|
|
|
<c:quotient>
|
|
<c:sum>
|
|
<c:value-of name="value" />
|
|
<t:negate>
|
|
<c:value-of name="skip" />
|
|
</t:negate>
|
|
</c:sum>
|
|
|
|
<c:value-of name="step" />
|
|
</c:quotient>
|
|
</function>
|
|
|
|
|
|
<!--
|
|
Calculates any arbitrary value given a vector of values and the step between
|
|
those values
|
|
|
|
This function is best demonstrated with an example. Consider the following
|
|
vector: [ 0, 5, 10, ..., 100 ]T. From this, we can see that the step is 5
|
|
and the skip is 0. (If the vector would have started at 5, then the skip
|
|
would have been 5.) The value is any arbitrary value.
|
|
|
|
This performs an index calculation and then calls vecstepi for further
|
|
processing; see vecstepi and vecpos for further information. Once the
|
|
relative position within the vector is calculated, we no longer need the
|
|
step and skip amounts, since they were only needed to determine the value's
|
|
position.
|
|
-->
|
|
<function name="vecstep" desc="Calculate a value that falls between a vector of values at a given step">
|
|
<param name="set" type="float" set="vector" desc="Vector of values" />
|
|
<param name="step" type="float" desc="Step between each of the values" />
|
|
<param name="skip" type="float" desc="Amount skipped before first element of vector" />
|
|
<param name="value" type="float" desc="Arbitrary value" />
|
|
|
|
<!-- call the function that will do the actual work -->
|
|
<c:apply name="vecstepi" set="set">
|
|
<!-- this is why the function call is necessary; we do not support
|
|
variable assignments, so the only way to have the same effect is to
|
|
invoke another function with the assignment as an argument (this
|
|
value is used frequently) -->
|
|
<c:arg name="pos">
|
|
<c:apply name="vecpos" step="step" skip="skip" value="value" />
|
|
</c:arg>
|
|
</c:apply>
|
|
</function>
|
|
|
|
|
|
<!--
|
|
Calculates any arbitrary value given a vector of values and an arbitrary
|
|
index
|
|
|
|
(Continuing from vecstep): if we were given the same vector [ 0, 5, 10, ...,
|
|
100 ]T and the calculated index 2 (assuming that the vectors are 0-indexed),
|
|
that would yield the same upper and lower value (5 and 5). The result would
|
|
then be ( 5 + ( 5-5 * 2-2 ) ) = 5. That's not very impressive, so let's
|
|
consider a more complicated case.
|
|
|
|
Consider that the given position is 2.5. By taking the floor and ceiling of
|
|
this position, we arrive at indexes 2 and 3, which yields the values 10 and
|
|
15 respectively. Our goal is to determine what the value (v) at position 2.5
|
|
should be:
|
|
- Start with the lower value; we'll be adding atop of this.
|
|
- Consider the remaining values (upper - lower): 15 - 10 = 5.
|
|
This means that we'll be adding some value between 0 and 5 such that the
|
|
value is 50% of the way (remember, we had 2.5) between the two. So,
|
|
we're looking for the value 2.5.
|
|
- To get this value, we can multiply the difference in the upper and lower
|
|
bounds by the decimal portion of our position (because 5 * 0.50 = 2.5).
|
|
To get the decimal portion, we can simply subtract the floor of the
|
|
position from the position itself: 2.5 - 2 = 0.5.
|
|
- So, we have: 5 + ( 15-10 * 2.5-2 ) = 5 + ( 5 * 0.5 ) = 5 + 2.5 = 7.5.
|
|
-->
|
|
<function name="vecstepi" desc="Calculate a value that falls between a vector of values at an arbitrary index">
|
|
<param name="set" type="float" set="vector" desc="Vector of values" />
|
|
<param name="pos" type="float" desc="Position of value within the vector (may be between values)" />
|
|
|
|
<c:sum>
|
|
<!-- add the closest lower value... -->
|
|
<c:value-of name="set">
|
|
<c:index>
|
|
<c:floor label="Lower index">
|
|
<c:value-of name="pos" />
|
|
</c:floor>
|
|
</c:index>
|
|
</c:value-of>
|
|
|
|
<c:product label="Partial value to apply atop of the lower base value">
|
|
<c:sum label="Subtract the upper and lower values to get the increment per step">
|
|
<c:value-of name="set" label="Upper value">
|
|
<c:index>
|
|
<c:ceil>
|
|
<c:value-of name="pos" />
|
|
</c:ceil>
|
|
</c:index>
|
|
</c:value-of>
|
|
|
|
<t:negate>
|
|
<c:value-of name="set" label="Lower value">
|
|
<c:index>
|
|
<c:floor>
|
|
<c:value-of name="pos" />
|
|
</c:floor>
|
|
</c:index>
|
|
</c:value-of>
|
|
</t:negate>
|
|
</c:sum>
|
|
|
|
<!-- determine the % between the two values -->
|
|
<c:sum label="Dropping the whole number from the position (yielding only the decimal) gives us the % difference">
|
|
<c:value-of name="pos" />
|
|
<t:negate>
|
|
<c:floor>
|
|
<c:value-of name="pos" />
|
|
</c:floor>
|
|
</t:negate>
|
|
</c:sum>
|
|
</c:product>
|
|
</c:sum>
|
|
</function>
|
|
</package>
|
|
|