diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/test/core/suite.xml | 1 | ||||
-rw-r--r-- | core/test/core/vector/filter.xml | 176 | ||||
-rw-r--r-- | core/vector/filter.xml | 110 |
3 files changed, 287 insertions, 0 deletions
diff --git a/core/test/core/suite.xml b/core/test/core/suite.xml index 146ef2e..c99bfe5 100644 --- a/core/test/core/suite.xml +++ b/core/test/core/suite.xml @@ -30,6 +30,7 @@ <import package="numeric/percent" /> <import package="numeric/round" /> + <import package="vector/filter" /> <import package="vector/fold" /> <import package="vector/interpolate" /> <import package="vector/length" /> diff --git a/core/test/core/vector/filter.xml b/core/test/core/vector/filter.xml new file mode 100644 index 0000000..c2a4442 --- /dev/null +++ b/core/test/core/vector/filter.xml @@ -0,0 +1,176 @@ +<?xml version="1.0"?> +<!-- + Copyright (C) 2014-2019 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" + desc="Testing minimum and maximum values of vectors"> + + <import package="../../spec" /> + + <import package="../../../base" /> + <import package="../../../vector/filter" /> + <import package="../../../vector/stub" /> + + + <const name="VFILTER_MASK_NONE" type="integer" + desc="4-vector masking nothing"> + <item value="1" desc="First set" /> + <item value="1" desc="Second set" /> + <item value="1" desc="Third set" /> + <item value="1" desc="Fourth set" /> + </const> + + <const name="VFILTER_MASK_MIDDLE" type="integer" + desc="4-vector with middle elements unmasked"> + <item value="0" desc="First unset" /> + <item value="1" desc="Second set" /> + <item value="1" desc="Third set" /> + <item value="0" desc="Fourth unset" /> + </const> + + + <t:describe name="_vfilter-mask_"> + <t:it desc="produces an empty vector given an empty vector"> + <t:given> + <c:length-of> + <!-- empty body with no @name@ --> + <t:vfilter-mask mask="NVEC1"> + </t:vfilter-mask> + </c:length-of> + </t:given> + + <t:expect> + <t:match-result eq="0" /> + </t:expect> + </t:it> + + + <t:it desc="acts as identity given an all-set mask"> + <t:given> + <c:length-of> + <t:vfilter-mask name="NVEC4_SEQ" mask="VFILTER_MASK_NONE" /> + </c:length-of> + </t:given> + + <t:expect> + <t:match-result eq="4" /> + </t:expect> + </t:it> + + + <t:it desc="retains original vector values"> + <t:given> + <c:let> + <c:values> + <c:value name="vec" type="integer" set="vector" + desc="Result"> + <t:vfilter-mask name="NVEC4_SEQ" mask="VFILTER_MASK_NONE" /> + </c:value> + </c:values> + + <c:sum of="vec" /> + </c:let> + </t:given> + + <t:expect> + <!-- 0 + 1 + 2 + 3 (NVEC4_SEQ) --> + <t:match-result eq="6" /> + </t:expect> + </t:it> + + + <t:it desc="produces empty vector given all-unset mask"> + <t:given> + <c:length-of> + <!-- NVEC4 is a 4-vector of 0s --> + <t:vfilter-mask name="NVEC4_SEQ" mask="NVEC4" /> + </c:length-of> + </t:given> + + <t:expect> + <t:match-result eq="0" /> + </t:expect> + </t:it> + + + <t:describe name="given a partly set mask"> + <t:it desc="removes masked vector elements"> + <t:given> + <c:length-of> + <t:vfilter-mask name="NVEC4_SEQ" mask="VFILTER_MASK_MIDDLE" /> + </c:length-of> + </t:given> + + <t:expect> + <t:match-result eq="2" /> + </t:expect> + </t:it> + + + <t:it desc="retains value of original vector elements"> + <t:given> + <c:let> + <c:values> + <c:value name="vec" type="integer" set="vector" + desc="Result"> + <t:vfilter-mask name="NVEC4_SEQ" mask="VFILTER_MASK_MIDDLE" /> + </c:value> + </c:values> + + <c:sum of="vec" /> + </c:let> + </t:given> + + <t:expect> + <!-- 1 + 2 --> + <t:match-result eq="3" /> + </t:expect> + </t:it> + + + <!-- same as above, but inline --> + <t:it desc="masks inline vectors"> + <t:given> + <c:let> + <c:values> + <c:value name="vec" type="integer" set="vector" + desc="Result"> + <!-- inline values --> + <t:vfilter-mask mask="VFILTER_MASK_MIDDLE"> + <c:value-of name="#10" /> + <c:value-of name="#12" /> + <c:value-of name="#14" /> + <c:value-of name="#16" /> + </t:vfilter-mask> + </c:value> + </c:values> + + <c:sum of="vec" /> + </c:let> + </t:given> + + <t:expect> + <!-- 12 + 14 --> + <t:match-result eq="26" /> + </t:expect> + </t:it> + </t:describe> + </t:describe> +</package> diff --git a/core/vector/filter.xml b/core/vector/filter.xml index e475516..ba6b034 100644 --- a/core/vector/filter.xml +++ b/core/vector/filter.xml @@ -54,6 +54,116 @@ <c:value-of name="vector_src" index="start_index" /> </t:cons-until-empty> </function> + + + \ref{_vfilter-mask_} allows filtering a vector using a boolean vector as + a mask. + If an index in the mask is~$0$, + then that corresponding index in the source vector will be removed. + The mask vector \should be the same length as the source vector.\footnote{ + Remember that TAME treats undefined values as~$0$.} + + <template name="_vfilter-mask_" + desc="Filter vector using a binary vector as a mask"> + <param name="@values@" desc="Inline vector" /> + <param name="@name@" desc="Named vector (in place of inline)" /> + <param name="@mask@" desc="Mask vector" /> + + + <c:apply name="_vfilter_mask" mask="@mask@"> + <c:arg name="vector"> + <if name="@name@"> + <c:value-of name="@name@" /> + </if> + <unless name="@name@"> + <c:vector> + <param-copy name="@values@" /> + </c:vector> + </unless> + </c:arg> + </c:apply> + </template> + + + <function name="_vfilter_mask" + desc="Filter source vector using binary vector as a mask"> + <param name="vector" type="float" set="vector" + desc="Source vector" /> + <param name="mask" type="integer" set="vector" + desc="Binary vector used to filter source vector" /> + + + <c:let> + <c:values> + <c:value name="length" type="integer" + desc="Length of source vector"> + <c:length-of> + <c:value-of name="vector" /> + </c:length-of> + </c:value> + + <!-- TODO: support constants in @index --> + <c:value name="curmask" type="integer" + desc="Current mask value"> + <c:value-of name="mask"> + <c:index> + <c:value-of name="#0" /> + </c:index> + </c:value-of> + </c:value> + </c:values> + + <c:cases> + <c:case label="No more elements in source vector"> + <t:when-eq name="length" value="#0" /> + + <c:vector /> + </c:case> + + + <c:case label="Skip non-match"> + <t:when-eq name="curmask" value="FALSE" /> + + <c:recurse> + <c:arg name="vector"> + <c:cdr> + <c:value-of name="vector" /> + </c:cdr> + </c:arg> + <c:arg name="mask"> + <c:cdr> + <c:value-of name="mask" /> + </c:cdr> + </c:arg> + </c:recurse> + </c:case> + + + <c:otherwise> + <c:cons> + <c:value-of name="vector"> + <c:index> + <c:value-of name="#0" /> + </c:index> + </c:value-of> + + <c:recurse> + <c:arg name="vector"> + <c:cdr> + <c:value-of name="vector" /> + </c:cdr> + </c:arg> + <c:arg name="mask"> + <c:cdr> + <c:value-of name="mask" /> + </c:cdr> + </c:arg> + </c:recurse> + </c:cons> + </c:otherwise> + </c:cases> + </c:let> + </function> </section> |