Combine core into this repository

tame-core used to be its own repository, but maintaining it separately does
not provide a whole lot of benefit, and further makes it difficult to see
version dependencies.  I also want to centralize documentation within this
project.
master v2.18.0
Mike Gerwitz 2018-09-11 09:29:36 -04:00
commit 44d3f76c0b
57 changed files with 10127 additions and 0 deletions

7
core/.gitignore vendored 100644
View File

@ -0,0 +1,7 @@
# when core is built
*.xmlo
*.xmle
*.js
*.dep
*.tmp
*.html

674
core/COPYING 100644
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program 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/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

57
core/README.md 100644
View File

@ -0,0 +1,57 @@
# TAME Core
Core library for TAME, providing generic abstractions for common
operations.
This library has accumulated a bit of cruft, is disorganized, and has
not been substantially refactored to take advantage of new language
features. It is a work in progress.
## Features
- BDD abstraction;
- Classification match manipulation;
- Common operations on numbers;
- Conditional evaluation helpers;
- Core primitive declarations;
- Interpolation;
- Interval mapping;
- Matrix and vector manipulation;
- Query matrices as data tables;
- Value mappings; and
- Other miscellaneous stuff.
## What is TAME?
TAME is The Adaptive Metalanguage, a programming language and system of tools
designed to aid in the development, understanding, and maintenance of systems
performing numerous calculations on a complex graph of dependencies,
conditions, and a large number of inputs.
This system was developed at R-T Specialty Buffalo to handle the complexity of
comparative insurance rating systems. It is a domain-specific language (DSL)
that itself encourages, through the use of templates, the creation of sub-DSLs.
TAME itself is at heart a calculator—processing only numerical input and
output—driven by quantifiers as predicates. Calculations and quantifiers are
written declaratively without concern for order of execution.
The system has powerful dependency resolution and data flow capabilities.
TAME consists of a macro processor (implementing a metalanguage), numerous
compilers for various targets (JavaScript, HTML documentation and debugging
environment, LaTeX, and others), linkers, and supporting tools. The input
grammar is XML, and the majority of the project (including the macro processor,
compilers, and linkers) are written in XSLT. There is a reason for that odd
choice; until an explanation is provided, know that someone is perverted enough
to implement a full compiler stack in XSLT.
## License
This program 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.

154
core/aggregate.xml 100644
View File

@ -0,0 +1,154 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="Aggregating Values">
<import package="base" export="true" />
<import package="vector/cmatch" export="true" />
Aggregate templates simplify aggregating values through various means.
Unless otherwise specified,
the default means of aggregation is summation.
<section title="Symbol-Based Aggregation">
For large numbers of values,
the most convenient way to aggregate is by matching on symbol names.
Note that symbols must be available for a match to occur.
All imported symbols are immediately available,
but \tt{expand-sequence} may need to be used for symbols produced by
the same package.
\ref{_aggregate-rate-each_} aggregates values of generators (usually
referred to by \tt{rate-each}) through summation.
A \tt{rate-each} block is generated to perform the summation.
Since \tt{rate-each} multiplies its body by \tt{_CMATCH_},
zero symbols would normally result in the summation of \tt{_CMATCH_}
itself, which is not desirable;
this template always includes \ref{ZERO} in the body to defend
against this,
causing a yield of~$0.00$ if there are no symbol matches.
<template name="_aggregate-rate-each_"
desc="Aggregate generator values by symbol prefix">
<param name="@class@" desc="Iterator class (omit for scalars)" />
<param name="@prefix@" desc="Symbol prefix" />
<param name="@yields@" desc="Scalar yield name (optional)">
<text></text>
</param>
<param name="@generates@" desc="Generator name (optional)">
<text></text>
</param>
<rate-each class="@class@" yields="@yields@"
generates="@generates@" index="k">
<c:sum>
<!-- prevent summing _CMATCH_ if there are no symbols (see above
comments) -->
<c:value-of name="ZERO"
label="Guard against zero symbol matches" />
<inline-template>
<for-each>
<sym-set name-prefix="@prefix@" type="gen" />
</for-each>
<c:value-of name="@sym_name@" index="k" />
</inline-template>
</c:sum>
</rate-each>
</template>
\ref{_aggregate-rate_} is analgous to \ref{_aggregate-rate-each_},
handling only scalar~\tt{@yields@}.
A \tt{rate} block is generated to aggregate by summation.
To prevent an empty rate block from being generated if there are no
symbol matches,
\ref{ZERO} is always included as part of the summation.
<template name="_aggregate-rate_"
desc="Aggregate scalar results by symbol prefix">
<param name="@prefix@" desc="Symbol prefix" />
<param name="@yields@" desc="Scalar yield name" />
<rate yields="@yields@">
<c:sum>
<!-- prevent completely empty rate block -->
<c:value-of name="ZERO"
label="Guard against zero symbol matches" />
<inline-template>
<for-each>
<sym-set name-prefix="@prefix@" type="rate" />
</for-each>
<c:value-of name="@sym_name@" />
</inline-template>
</c:sum>
</rate>
</template>
\ref{_aggregate-classify_} aggregates classifications.
Keep in mind that classifications act as universal quantifiers by default,
meaning zero symbol matches will produce a match and a scalar~$1$;
existential quantifiers (\tt{@any@} set to \tt{true}) will \emph{not}
match and will produce the scalar~$0$.
<template name="_aggregate-classify_"
desc="Aggregate classification results by symbol prefix">
<param name="@prefix@" desc="Symbol prefix" />
<param name="@as@" desc="Classification name" />
<param name="@desc@" desc="Generated classification description" />
<param name="@yields@" desc="Vector yield name (optional)">
<text></text>
</param>
<param name="@any@"
desc="Existential classification (default false, universal)">
<text></text>
</param>
<classify as="@as@" yields="@yields@" desc="@desc@" any="@any@">
<inline-template>
<for-each>
<sym-set name-prefix="@prefix@" type="class" />
</for-each>
<t:match-class name="@sym_name@" />
</inline-template>
</classify>
</template>
</section>
</package>

100
core/alias.xml 100644
View File

@ -0,0 +1,100 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2017 R-T Specialty, 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"
core="true"
desc="Aliasing Values">
These alias templates allow giving a new name to existing values.
They are most useful for avoiding too many template-generated variables.
If an alias name matches the name of the source,
then no alias will be generated;
this behavior is useful when generating aliases in templates where the
caller may or may not choose a name that would otherwise conflict with
its internal representation.
If no description is provided,
aliases will inherit the description from the source symbol.
<template name="_classify-alias_"
desc="Alias classification">
<param name="@as@" desc="Classification alias name" />
<param name="@from@" desc="Source classification" />
<param name="@desc@" desc="Classification alias description">
<param-sym-value prefix=":class:" name="@from@" value="desc" />
</param>
<param name="@yields@" desc="Classification alias yield">
<text></text>
</param>
<param name="@__src_yields@" desc="Source classification yield">
<param-class-to-yields name="@from@" />
</param>
<unless name="@as@" eq="@from@">
<classify as="@as@" desc="@desc@" yields="@yields@">
<match on="@__src_yields@" />
</classify>
</unless>
</template>
<template name="_rate-alias_"
desc="Alias calculation">
<param name="@yields@" desc="Alias name" />
<param name="@from@" desc="Source name" />
<unless name="@from@" eq="@yields@">
<rate yields="@yields@">
<c:value-of name="@from@" />
</rate>
</unless>
</template>
<template name="_rate-each-alias_"
desc="Alias generator">
<param name="@generates@" desc="Generator alias name" />
<param name="@from@" desc="Source generator" />
<param name="@desc@" desc="Generator alias description">
<param-sym-value name="@from@" value="desc" />
</param>
<param name="@yields@" desc="Yield alias name">
<text>_</text>
<param-value name="@generates@" />
</param>
<unless name="@generates@" eq="@from@">
<rate yields="@yields@">
<c:sum of="@from@" desc="@desc@" generates="@generates@" index="k">
<c:value-of name="@from@" index="k" />
</c:sum>
</rate>
</unless>
</template>
</package>

110
core/assert.xml 100644
View File

@ -0,0 +1,110 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2015, 2018 R-T Specialty, 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:t="http://www.lovullo.com/rater/apply-template"
core="true"
desc="Assertions">
<import package="base" />
<import package="vector/cmatch" export="true" />
This package is young;
the intent is to provide basic assertions to ensure data
integrity.
\emph{N.B. The behavior of this package changed in version 1.0.0---%
\ref{_assert_} now properly fails on non-match,
not match.}
The \ref{_assert_} template generates a~generic assertion using the
provided predicates.
If the predicates provided to the assertion fail (yields $\bot$),
the system immediately terminates.\footnote{
Beacuse the system stops processing once a terminating classification
yields~$\top$,
only one assertion will ever match,
even if others would match if execution were to continue.}
\tt{@class@} may optionally be used to predicate the asseriton itself---%
the assertion will be performed only on respective class matches.
\ref{_assert_} implements assertions by genearting two classifications---%
one to perform the actual assertion,
and a terminating classification to ensure that the assertion
yields~$\top$.
The param \ref{assert_ignore} will suppress assertion failures at runtime.
<param name="assert_ignore" type="boolean" default="0"
desc="Ignore assertion failures" />
<template name="_assert_"
desc="Terminate on predicate failure">
<param name="@values@" desc="Predicates" />
<param name="@as@" desc="Generated classification name">
<text unique="true">-assert-</text>
</param>
<param name="@failure@" desc="Failure description">
<text>Assertion</text>
</param>
<param name="@class@" desc="Only perform assertion for respective class
matches (optional)" />
<param name="@neg_as@"
desc="Generated name for classification to be negated">
<text unique="true">-nassert-</text>
</param>
<param name="@neg_yields@"
desc="Generated yield for classification to be negated">
<text unique="true">_nassert</text>
</param>
<!-- The actual assertion will be performed by one classification... -->
<classify as="@neg_as@" yields="@neg_yields@"
desc="{@failure@} (assertion result)">
<any>
<!-- if class is provided and does not match, then the assertion
succeeds automatically -->
<if name="@class@">
<t:match-class name="@class@" value="FALSE" />
</if>
<all>
<param-copy name="@values@" />
</all>
</any>
</classify>
<!-- ...which is in turn negated for the terminating
classification. The reason for this is that terminating
classifications terminate on _match_. -->
<classify as="@as@" desc="@failure@" terminate="true">
<match on="assert_ignore" value="FALSE" />
<match on="@neg_yields@" value="FALSE" />
</classify>
</template>
</package>

220
core/base.xml 100644
View File

@ -0,0 +1,220 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2015, 2017, 2018 R-T Specialty, 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"
core="true"
desc="Base features">
The \pkgself~package exposes common and internal
defintions. Ideally, this package will be included automatically by
the compiler to remove repetitive, boilerplate imports. Importing
this package isn't necessary if none of these definitions are
needed.
<section title="Internal Constants">
\ref{_CMATCH_} is a magic constant that contains the result of
a~classification match. This is used implicity by
\ref{rate-each}.\footnote{The symbol is \Xi~because it looks like
a sideways array.}
\todo{Remove in favor of a local variable or generated
classification; there is no need (anymore) for this to be magic.}
<const name="_CMATCH_" type="boolean" sym="\Xi"
desc="Classification match vector (applicability)">
<item value="0"
desc="Dummy value; this set is populated upon entering
each rate block" />
</const>
The runtime is responsible for populating \ref{__DATE_YEAR__} with
a proper value representing the current year.
\todo{TAME is deterministic with this one exception; remove it and
have users use the params from {\tt datetime} instead if they need this
datum.}
<const name="__DATE_YEAR__" magic="true"
value="0" type="integer"
desc="Current year"
sym="\widehat{D^\gamma}" />
</section>
<section title="Primitive Types">
Primitives are defined internally; these definitions simply
provide symbols to permit their use.
<typedef name="integer"
desc="Any value in the set of integers"
sym="\mathbb{I}">
<base-type />
</typedef>
<typedef name="float"
desc="Any real number (represented as a float)"
sym="\mathbb{R}">
<base-type />
</typedef>
\ref{empty} does not have much use outside of the compiler.
<typedef name="empty"
desc="Empty set"
sym="\emptyset">
<base-type />
</typedef>
</section>
<section title="Boolean and Unknown">
\ref{boolean} contains the boolean \ref{TRUE} and~\ref{FALSE} values,
which map to~$1$ and~$0$ respectively.
The \ref{maybe} type is the union of \ref{boolean} and \ref{NOTHING},
with a value of~$-1$;\footnote{
This is similar in spirit to the Haskell \tt{Maybe} type,
or the OCaml \tt{Option} type.
}this is commonly used to represent an unknown state or missing
value.\footnote{
The \ref{nothing}~type is used for the sake of the union;
it should not be used directly.}
<typedef name="maybe" desc="Boolean or unknown value">
<union>
<typedef name="nothing" desc="Unknown value">
<enum type="integer">
<item name="NOTHING" value="-1" desc="Unknown or missing value" />
</enum>
</typedef>
<typedef name="boolean" desc="Boolean values">
<enum type="integer">
<item name="TRUE" value="1" desc="True" />
<item name="FALSE" value="0" desc="False" />
</enum>
</typedef>
</union>
</typedef>
The constant \ref{UNKNOWN} is also defined as~$-1$ to serve as an
alternative to the term~``nothing''.
<const name="UNKNOWN" value="-1"
desc="Unknown or missing value" />
</section>
<section title="Convenience">
$0$~is a~common value. Where a value is required (such
as a~template argument), \ref{ZERO} may be used. TAME now
supports a~constant-scalar syntax ({\tt #0}; \todo{reference this
in documentation}), making this largely unnecessary.
This is declared as a float to provide compatibility with all
types of expressions.
<const name="ZERO" value="0.00"
desc="Zero value" />
In the case where classifications are required, but a~static
assumption about the applicability of the subject can be made, we
have values that are always~true and always~false. The use
of~\ref{never} may very well be a~code smell, but let us not rush
to judgment.\footnote{\ref{never} has been added as an analog
to~\ref{always}; its author has never had use for it. Oh, look,
we just used ``never''.}
<classify as="always"
desc="Always true"
yields="alwaysTrue"
keep="true" />
<classify as="never"
any="true"
desc="Never true"
yields="neverTrue"
keep="true" />
</section>
<section title="Work-In-Progress">
\ref{_todo_} formalizes TODO items and may optionally yield a
value~\tt{@value@} for use within calculations.%
\footnote{This is different than its previous behavior of always
yielding a scalar~$0$.}
All uses of the \ref{_todo_} template will produce a warning composed of
its description~\tt{@desc@}.
<template name="_todo_"
desc="Represents work that needs to be done">
<param name="@desc@" desc="TODO desc">
<text>TODO</text>
</param>
<param name="@value@" desc="Placeholder value" />
<param name="@index@" desc="Placeholder value index">
<text></text>
</param>
<unless name="@value@">
<unless name="@index@" eq="">
<error>Using @index@ without @value@</error>
</unless>
</unless>
<warning>
TODO: <param-value name="@desc@" />
</warning>
<if name="@value@">
<c:value-of name="@value@" index="@index@" />
</if>
</template>
The \ref{_ignore_} template serves as a~block
comment.\footnote{This is useful since XML does not support nested
comments, which makes it difficult to comment out code that
already has XML comments.} It may be useful for debugging, but is
discouraged for use otherwise. The \ref{_ignore_/@desc@} param
should be used to describe intent.
<template name="_ignore_"
desc="Removes all child nodes (as if commented out)">
<param name="@values@" desc="Nodes to comment out" />
<param name="@desc@" desc="Reason for ignore" />
<warning>Ignored block!</warning>
</template>
</section>
</package>

169
core/cond.xml 100644
View File

@ -0,0 +1,169 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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"
core="true"
desc="Generic conditionals">
<import package="base" />
<!-- TODO: use reduce once we have support for function passing -->
<function name="or" desc="Return value A if non-zero, otherwise B">
<param name="or_a" type="float" desc="Value to return if non-zero" />
<param name="or_b" type="float" desc="Value to return if A is zero" />
<c:cases>
<!-- return B if A is zero -->
<c:case>
<c:when name="or_a">
<c:eq>
<c:const value="0" type="integer" desc="Return B if A is 0" />
</c:eq>
</c:when>
<c:value-of name="or_b" />
</c:case>
<!-- return A if non-zero -->
<c:otherwise>
<c:value-of name="or_a" />
</c:otherwise>
</c:cases>
</function>
<template name="_cond_" desc="Conditional shorthand">
<param name="@name@" desc="Value to check" />
<param name="@index@" desc="Index of boolean value" />
<param name="@value@" desc="Constant value" />
<param name="@type@" desc="Value type" />
<param name="@cond@" desc="Value to compare against (default boolean true)">
<text>TRUE</text>
</param>
<param name="@desc@" desc="Value description">
<text>Value when </text>
<param-value name="@name@" />
</param>
<c:const value="@value@" type="@type@" desc="@desc@">
<!-- TODO: non-index option -->
<c:when name="@name@" index="@index@">
<c:eq>
<c:value-of name="@cond@" />
</c:eq>
</c:when>
</c:const>
</template>
<template name="_yield-unless_" desc="Yield a value unless another value is set">
<param name="@name@" desc="Overriding value" />
<param name="@index@" desc="Index" />
<param name="@values@" desc="Value to use otherwise" />
<c:cases>
<c:case>
<if name="@index@">
<c:when name="@name@" index="@index@">
<c:gt>
<c:const value="0" type="integer" desc="Use override if greater than 0" />
</c:gt>
</c:when>
</if>
<unless name="@index@">
<c:when name="@name@">
<c:gt>
<c:const value="0" type="integer" desc="Use override if greater than 0" />
</c:gt>
</c:when>
</unless>
<if name="@index@">
<c:value-of name="@name@" index="@index@" />
</if>
<unless name="@index@">
<c:value-of name="@name@" />
</unless>
</c:case>
<c:otherwise>
<param-copy name="@values@" />
</c:otherwise>
</c:cases>
</template>
<template name="_default_" desc="Set a default value if a value is not set">
<param name="@name@" desc="Param name" />
<param name="@index@" desc="Index" />
<param name="@default@" desc="Default value to use if empty" />
<c:cases>
<c:case>
<c:when name="@name@" index="@index@">
<c:eq>
<c:const value="0" type="integer" desc="No value" />
</c:eq>
</c:when>
<c:const value="@default@" type="integer" desc="Default value" />
</c:case>
<c:otherwise>
<c:value-of name="@name@" index="@index@" />
</c:otherwise>
</c:cases>
</template>
<template name="_cond-value-each_" desc="Conditional value">
<param name="@class@" desc="Class match" />
<param name="@generates@" desc="Variable to generate into" />
<param name="@value@" desc="Value to yield (use either this or const)" />
<param name="@const@" desc="Constant to yield (use either this or value)" />
<param name="@count@" desc="Optional number of times to apply const/value (as a variable)" />
<param name="@desc@" desc="Optional constant description">
<text>Constant value</text>
</param>
<!-- simply returns a constant value for the class match -->
<rate-each class="@class@" accumulate="none" generates="@generates@" index="k">
<c:product>
<if name="@value@">
<c:value-of name="@value@" />
</if>
<unless name="@value@">
<c:const value="@const@" type="float" desc="@desc@" />
</unless>
<!-- if this is not provided, then the c:product will be optimized away -->
<if name="@count@">
<c:value-of name="@count@" index="k" />
</if>
</c:product>
</rate-each>
</template>
</package>

View File

@ -0,0 +1,59 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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"
title="Definition Conventions">
\ref{_naming-convention_} defines a naming convention for a parameter that
is enforced at compile-time.
Conventions may have prefixes, suffixes, or both.
If violated,
the compiler will abort in error.
<template name="_naming-convention_"
desc="Error about naming convention violation">
<param name="@name@" desc="Given name" />
<param name="@prefix@" desc="Required prefix" />
<param name="@suffix@" desc="Required suffix" />
<if name="@prefix@">
<unless name="@name@" prefix="@prefix@">
<error>
naming convention violation:
`<param-value name="@name@" />' must be prefixed with
`<param-value name="@prefix@" />'.
</error>
</unless>
</if>
<if name="@suffix@">
<unless name="@name@" suffix="@suffix@">
<error>
naming convention violation:
`<param-value name="@name@" />' must be suffixed with
`<param-value name="@suffix@" />'.
</error>
</unless>
</if>
</template>
</package>

135
core/datetime.xml 100644
View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015, 2017 R-T Specialty, 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:t="http://www.lovullo.com/rater/apply-template"
xmlns:c="http://www.lovullo.com/calc"
core="true"
desc="Date and time">
<import package="base" />
<import package="assert" />
<import package="vector/cmatch" />
Providing the current timestamp externally allows the system to be
wholly deterministic.
It also captures the state of the data in time,
so re-calculating in the future (e.g. using the summary pages)
will yield the same result.
<param name="timestamp_current" type="integer" default="0"
desc="Unix timestamp representing the current time of the
system (clock time), as it should be perceived" />
<t:assert failure="Missing Unix timestamp for timestamp_current">
<t:match-gt on="timestamp_current" value="ZERO" />
</t:assert>
This system does not handle its own arthiemtic to figure out the
current date components from the given timestamp.
<param name="timestamp_year" type="integer" default="0"
desc="Current year (clock time), as it should be perceived" />
<param name="timestamp_month" type="integer" default="0"
desc="Current month (clock time), as it should be perceived;
1-indexed" />
<param name="timestamp_day" type="integer" default="0"
desc="Current day (clock time), as it should be perceived;
1-indexed" />
<!--
Determines how many years the given date is relative to the current date
Note that a positive number will be returned if the given date is
in the past, negative if in the future.
-->
<template name="_age-years_" desc="Age in years of the given date relative to the current">
<param name="@generates@" desc="Generator variable, per index" />
<param name="@yearset@" desc="Set of years to iterate on" />
<param name="@sym@" desc="Generator symbol" />
<param name="@when@" desc="Only when this boolean value is true" />
<param name="@default@" desc="Default age when yearset values are empty" />
<param name="@yields@" desc="Variable to yield into">
<text>_</text>
<param-value name="@generates@" />
</param>
<rate yields="@yields@">
<c:sum of="@yearset@" index="k" generates="@generates@" desc="Relative age" sym="@sym@">
<c:cases>
<if name="@when@">
<c:case>
<c:when name="@when@" index="k">
<c:eq>
<c:value-of name="FALSE" />
</c:eq>
</c:when>
<!-- return a default, or 0 if no default is provided -->
<if name="@default@">
<c:value-of name="@default@" index="k" />
</if>
<unless name="@default@">
<c:const value="0" type="integer" desc="Condition not met, but no default" />
</unless>
</c:case>
</if>
<!-- if no @when@, then we'll always do this -->
<c:otherwise>
<c:cases>
<c:case>
<c:when name="@yearset@" index="k">
<c:gt>
<c:const value="0" type="integer" desc="Only calculate difference if a value is available" />
</c:gt>
</c:when>
<c:sum label="Calculate by subtracting the given year from the current year">
<c:value-of name="timestamp_year" />
<c:product>
<c:value-of name="NEGATE" />
<c:value-of name="@yearset@" index="k" />
</c:product>
</c:sum>
</c:case>
<!-- we don't have a value; just use the default age we were given -->
<if name="@default@">
<c:otherwise>
<c:value-of name="@default@" index="k" />
</c:otherwise>
</if>
</c:cases>
</c:otherwise>
</c:cases>
</c:sum>
</rate>
</template>
</package>

87
core/dummy.xml 100644
View File

@ -0,0 +1,87 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2016 R-T Specialty, 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"
core="true"
title="Dummy Values">
These are dummy values created primarily for testing.
Vectors are required for iteration;
the below generate values useful for doing so in constant,
calculation, and classification form.
<const name="VEC1" desc="Vector of length 1">
<item value="1" desc="Dummy value" />
</const>
<const name="VEC2" desc="Vector of length 2">
<item value="1" desc="Dummy value" />
<item value="2" desc="Dummy value" />
</const>
<const name="VEC3" desc="Vector of length 3">
<item value="1" desc="Dummy value" />
<item value="2" desc="Dummy value" />
<item value="3" desc="Dummy value" />
</const>
<rate yields="_vec1">
<c:sum of="VEC1" generates="vec1" index="k"
desc="Vector of length 1">
<c:const value="1" desc="Dummy" />
</c:sum>
</rate>
<rate yields="_vec2">
<c:sum of="VEC2" generates="vec2" index="k"
desc="Vector of length 2">
<c:const value="1" desc="Dummy" />
</c:sum>
</rate>
<rate yields="_vec3">
<c:sum of="VEC3" generates="vec3" index="k"
desc="Vector of length 3">
<c:const value="1" desc="Dummy" />
</c:sum>
</rate>
<classify as="length1" desc="Classification of length 1">
<match on="vec1">
<c:gt>
<c:const value="0" desc="Check if defined" />
</c:gt>
</match>
</classify>
<classify as="length2" desc="Classification of length 2">
<match on="vec2">
<c:gt>
<c:const value="0" desc="Check if defined" />
</c:gt>
</match>
</classify>
<classify as="length3" desc="Classification of length 3">
<match on="vec3">
<c:gt>
<c:const value="0" desc="Check if defined" />
</c:gt>
</match>
</classify>
</package>

80
core/extern.xml 100644
View File

@ -0,0 +1,80 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2017 R-T Specialty, 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"
core="true"
desc="Extern Definition">
Externs are symbols that are expected to be defined by package importers
before linking.
Rather than explicitly declaring extern symbols,
these templates provide more human-readable abstractions.
Note that when defining extern classifications with
\ref{_classify-extern_},
if specifying a yield,
the dimensionality is~$1$ unless otherwise provided.
<template name="_classify-extern_"
desc="Define extern for classification">
<param name="@as@" desc="Extern classification name" />
<param name="@yields@" desc="Extern classification yield">
<text></text>
</param>
<!-- default assumption is a vector -->
<param name="@dim@" desc="Extern classification yield dimensions">
<text>1</text>
</param>
<extern name=":class:{@as@}" type="class" dim="0"
yields="@yields@" />
<if name="@yields@">
<extern name="@yields@" type="cgen" dim="@dim@"
parent=":class:{@as@}" />
</if>
</template>
<template name="_rate-each-extern_"
desc="Define extern for rate-each">
<param name="@generates@" desc="Extern generator name" />
<param name="@yields@" desc="Extern yield name" />
<if name="@generates@">
<extern name="@generates@" type="gen" dtype="float" dim="1" />
</if>
<if name="@yields@">
<extern name="@yields@" type="rate" dtype="float" dim="0" />
</if>
</template>
<template name="_rate-extern_"
desc="Define extern for rate">
<param name="@yields@" desc="Extern yield name" />
<extern name="@yields@" type="rate" dtype="float" dim="0" />
</template>
</package>

423
core/insurance.xml 100644
View File

@ -0,0 +1,423 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2016, 2018 R-T Specialty, 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"
title="Insurance Abstractions">
<import package="base" />
<import package="assert" export="true" />
<import package="convention" export="true" />
<import package="map" export="true" />
<import package="numeric/round" export="true" />
<import package="vector/cmatch" export="true" />
These are primitive abstractions for insurance that will be
improved upon over time.
Some notable TODOs:
\begin{enumerate}
\item Enforce naming convention;
\item Support scalar results;
\item Fail on zero premium unless explicitly stated;
\item Fail on negative premium (use a credit template);
\item Rounding direction (currently only nearest); and
\item Credit and surcharge.
\end{enumerate}
\todo{Template to abstract these {\tt rate-each} generation
templates.}
<template name="_premium_"
desc="A premium dollar amount">
<param name="@values@" desc="Body" />
<param name="@class@" desc="Predicate" />
<param name="@generates@" desc="Generator name" />
<param name="@index@" desc="Generator index" />
<param name="@no@" desc="Negated predicate">
<text></text>
</param>
<param name="@sym@" desc="TeX symbol">
<text></text>
</param>
<param name="@gensym@" desc="Generator TeX symbol">
<text></text>
</param>
<!-- required since various companies have various bizzare
rounding rules that tend to be a source of bugs; make the
developer think about it -->
<param name="@round@" desc="Rounding method" />
<!-- not yet used, but it will at least serve as code
documentation for the time being -->
<param name="@desc@" desc="Premium description" />
<rate-each class="@class@" no="@no@"
generates="@generates@" index="@index@"
sym="@sym@" gensym="@gensym@">
<!-- TODO: we now have reason for a more concise conditional
syntax -->
<if name="@round@" eq="dollar">
<t:round>
<param-copy name="@values@" />
</t:round>
</if>
<unless name="@round@" eq="dollar">
<if name="@round@" eq="cent">
<c:apply name="round_cents">
<c:arg name="round_cents_val">
<param-copy name="@values@" />
</c:arg>
</c:apply>
</if>
<unless name="@round@" eq="cent">
<if name="@round@" eq="up">
<c:ceil>
<param-copy name="@values@" />
</c:ceil>
</if>
<unless name="@round@" eq="up">
<if name="@round@" eq="down">
<c:floor>
<param-copy name="@values@" />
</c:floor>
</if>
<unless name="@round@" eq="down">
<!-- no rounding -->
<param-copy name="@values@" />
</unless>
</unless>
</unless>
</unless>
</rate-each>
</template>
\ref{_factor_} defines a calculation that results in a factor
which will later be used in a product.
There are special considerations for these types of values---%
generally, they should not have a value of~$0$ if some sort of calculation
condition or lookup is not met,
as that would have the effect of wiping out premium.\footnote{
Note that every generator index can be non-zero but still sum up
to a zero yield,
which would trigger this error.}
If zero is desired,
\tt{@allow-zero@} must be set to \tt{true} to explicitly permit it.
<template name="_factor_"
desc="Factor to multiply against (must be non-zero by default)">
<param name="@values@" desc="Body" />
<param name="@class@" desc="Predicate">
<text></text>
</param>
<param name="@generates@" desc="Generator name">
<text></text>
</param>
<param name="@index@" desc="Generator index">
<text></text>
</param>
<param name="@no@" desc="Negated predicate">
<text></text>
</param>
<param name="@sym@" desc="TeX symbol">
<text></text>
</param>
<param name="@gensym@" desc="Generator TeX symbol">
<text></text>
</param>
<param name="@yields@" desc="Yield (optional)">
<text></text>
</param>
<!-- at least one of generates or yields is required -->
<if name="@yields@" eq="">
<if name="@generates@" eq="">
<error>must provide at least one of @generates or @yields</error>
</if>
</if>
<!-- not yet used, but it will at least serve as code
documentation for the time being -->
<param name="@desc@" desc="Factor description" />
<unless name="@desc@">
<unless name="@generates@" eq="">
<error>
a description (@desc@) is required for
`<param-value name="@generates@" />'
</error>
</unless>
<if name="@generates@" eq="">
<error>
a description (@desc@) is required for
`<param-value name="@yields@" />'
</error>
</if>
</unless>
<!-- normally we want factors to default to 1, otherwise they could wipe
out premium -->
<param name="@allow-zero@" desc="Allow value of zero (default false; see
also @default@)">
<text>false</text>
</param>
<!-- default is _only_ used when a factor is 0, so it makes no sense to
set a default to #0 -->
<param name="@default@" desc="Default value if 0 (optional)" />
<if name="@default@" eq="#0">
<error>
a value of #0 for @default@ is not meaningful;
use @allow-zero@ instead.
</error>
</if>
<!-- negative values might be indicitive of a failure to provide a floor
on certain factors when summing (for example) -->
<param name="@allow-negative@" desc="Allow negative value (default false)">
<text>false</text>
</param>
<param name="@_prefix@" desc="Factor type (drives naming convention)">
<text>factor</text>
</param>
<unless name="@generates@" eq="">
<t:naming-convention name="@generates@" prefix="@_prefix@" />
</unless>
<unless name="@yields@" eq="">
<t:naming-convention name="@yields@" prefix="@_prefix@" />
</unless>
<!-- factor calculation -->
<rate-each class="@class@" no="@no@" yields="@yields@" sym="@sym@"
generates="@generates@" index="@index@" gensym="@gensym@">
<!-- use a default if provided if the factor expression yields 0 -->
<if name="@default@">
<c:let>
<c:values>
<c:value name="factor" type="float"
desc="Factor result before default">
<param-copy name="@values@" />
</c:value>
</c:values>
<t:map-set name="factor">
<t:map from="ZERO" value="@default@" />
<t:map-else value="factor" />
</t:map-set>
</c:let>
</if>
<!-- avoid let generation if no default was provided -->
<unless name="@default@">
<param-copy name="@values@" />
</unless>
</rate-each>
<!-- assertion for non-zero -->
<unless name="@allow-zero@" eq="true">
<!-- assertions are useless if a static default was provided, since we
know that zero can then never be yielded-->
<unless name="@default@" prefix="#">
<unless name="@generates@" eq="">
<t:assert failure="{@generates@} must not yield a value of 0 for
any index">
<t:match-ne on="@generates@" value="ZERO" />
</t:assert>
</unless>
<unless name="@yields@" eq="">
<t:assert failure="{@yields@} must not yield a value of 0">
<t:match-ne on="@yields@" value="ZERO" />
</t:assert>
</unless>
</unless>
</unless>
<!-- assertion for non-negative -->
<unless name="@allow-negative@" eq="true">
<unless name="@generates@" eq="">
<t:assert failure="{@generates@} must not yield a negative value
for any index">
<t:match-gt on="@generates@" value="ZERO" />
</t:assert>
</unless>
<unless name="@yields@" eq="">
<t:assert failure="{@yields@} must not yield a negative value">
<t:match-gt on="@yields@" value="ZERO" />
</t:assert>
</unless>
</unless>
</template>
The \ref{_credit_} and \ref{_debit_} templates define factors that are
intended to introduce, respectively, credits and surcharges.
Each name defined using these templates must be prefixed with ``credit''
and ``debit'' respectively.
Credits must never exceed a value of~$1$,
as they would then be treated as if they were surcharges.
Similarly, debits must never have a value less than~$1$.
Both must have values greater than~$0$.
If a value can be either a credit or a debit,
it is sometimes called an ``adjustment''.
In reality it is just another factor,
so just use \ref{_factor_} in such a~case.
<template name="_credit_"
desc="Credit to be used as a factor">
<param name="@values@" desc="Credit calculation" />
<param name="@class@" desc="Predicate">
<text></text>
</param>
<param name="@generates@" desc="Generator name">
<text></text>
</param>
<param name="@index@" desc="Generator index">
<text></text>
</param>
<param name="@no@" desc="Negated predicate">
<text></text>
</param>
<param name="@sym@" desc="TeX symbol">
<text></text>
</param>
<param name="@gensym@" desc="Generator TeX symbol">
<text></text>
</param>
<param name="@yields@" desc="Yield (optional)">
<text></text>
</param>
<param name="@desc@" desc="Factor description">
<text></text>
</param>
<t:factor _prefix="credit"
class="@class@" no="@no@" yields="@yields@" sym="@sym@"
generates="@generates@" index="@index@" gensym="@gensym@"
desc="@desc@">
<param-copy name="@values@" />
</t:factor>
<unless name="@generates@" eq="">
<t:assert failure="{@generates@} is a credit but one or more
of its values are greater than 1">
<t:match-lte on="@generates@" value="#1" />
</t:assert>
</unless>
<unless name="@yields@" eq="">
<t:assert failure="{@yields@} is a credit but its value is
greater than 1">
<t:match-lte on="@yields@" value="#1" />
</t:assert>
</unless>
</template>
<template name="_debit_"
desc="Debit to be used as a factor">
<param name="@values@" desc="Credit calculation" />
<param name="@class@" desc="Predicate">
<text></text>
</param>
<param name="@generates@" desc="Generator name">
<text></text>
</param>
<param name="@index@" desc="Generator index">
<text></text>
</param>
<param name="@no@" desc="Negated predicate">
<text></text>
</param>
<param name="@sym@" desc="TeX symbol">
<text></text>
</param>
<param name="@gensym@" desc="Generator TeX symbol">
<text></text>
</param>
<param name="@yields@" desc="Yield (optional)">
<text></text>
</param>
<param name="@desc@" desc="Factor description">
<text></text>
</param>
<t:factor _prefix="debit"
class="@class@" no="@no@" yields="@yields@" sym="@sym@"
generates="@generates@" index="@index@" gensym="@gensym@"
desc="@desc@">
<param-copy name="@values@" />
</t:factor>
<unless name="@generates@" eq="">
<t:assert failure="{@generates@} is a debit but one or more
of its values are less than 1">
<t:match-gte on="@generates@" value="#1" />
</t:assert>
</unless>
<unless name="@yields@" eq="">
<t:assert failure="{@yields@} is a credit but its value is
less than 1">
<t:match-gte on="@yields@" value="#1" />
</t:assert>
</unless>
</template>
</package>

188
core/map.xml 100644
View File

@ -0,0 +1,188 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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/>.
-->
<!--
Brings c:case statements to life
The problem with cases is that they are so verbose for simple cases. This
makes simple cases simple.
-->
<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"
title="Map Sets">
The problem with \texttt{case} statements is that they are very
verbose, which is greatly distracting for smaller cases, and~can
take away from the actual intent of the~code. Map~sets make simple
cases simple by allowing concise map definitions.
If \ref{_map-set_/@name@} is provided, then each map implicitly
operates on that reference as its input, unless overridden using
\ref{_map_/@name@}. \ref{_map-set_/@default@} can be used to
provide a constant default if no mapping is otherwise available; see
also~\ref{_map-else_}.
<template name="_map-set_" desc="Map a set of values">
<param name="@name@" desc="Param name" />
<param name="@index@" desc="Index" />
<param name="@values@" desc="Criteria (map nodes)" />
<param name="@label@" desc="Case statement label">
<!-- default empty -->
<text></text>
</param>
<!-- used in param-meta for _map-else_, so let's make the default
clear -->
<param name="@default@" desc="Default value">
<text></text>
</param>
<c:cases label="@label@">
<param-copy name="@values@">
<param-meta name="map_param" value="@name@" />
<param-meta name="map_index" value="@index@" />
<param-meta name="map_default" value="@default@" />
</param-copy>
<unless name="@default@" eq="">
<c:otherwise>
<c:const value="@default@" type="integer" desc="No mapping" />
</c:otherwise>
</unless>
</c:cases>
</template>
Mappings are defined using \ref{_map_}. The input defaults to
the~reference declared by~\ref{_map-set_/@name@},
if~provided. \ref{_map_/@name@} takes precedence, even if
the~former is provided. Just like \texttt{case}~statements,
multiple inputs can be provided by specifying multiple references;
this means that each \ref{_map_/@name@} can differ.\footnote{This
isn't particularly intuitive or recommended, but it does simplify
certain tasks enough to maybe justify this type of use.}
Just like \texttt{case}~statements themselves, maps are
surjective---the codomain implicitly includes $0$~to handle all
default cases, unless a default is provided.
<template name="_map_" desc="Map criteria">
<param name="@from@" desc="Value to map from" />
<param name="@value@" desc="Constant to map to" />
<param name="@to@" desc="Named value (use instead of @value@)" />
<param name="@to-index@" desc="Named value index">
<text></text>
</param>
<param name="@type@" desc="Constant value type">
<text>float</text>
</param>
<param name="@desc@" desc="Map description">
<text>Destination mapping</text>
</param>
<!-- set by parent map-set -->
<param name="@name@" desc="Param name">
<param-inherit meta="map_param" />
</param>
<param name="@index@" desc="Index">
<param-inherit meta="map_index" />
</param>
<c:case>
<!-- index provided -->
<if name="@index@">
<c:when name="@name@" index="@index@">
<c:eq>
<c:value-of name="@from@" />
</c:eq>
</c:when>
</if>
<!-- no index provided -->
<unless name="@index@">
<c:when name="@name@">
<c:eq>
<c:value-of name="@from@" />
</c:eq>
</c:when>
</unless>
<if name="@value@">
<c:const value="@value@" desc="@desc@" />
</if>
<unless name="@value@">
<c:value-of name="@to@" index="@to-index@" />
</unless>
</c:case>
</template>
The default condition can be handled in two different ways: via
\ref{_map-set_/@default@}, or using \ref{_map-else_}; the latter
provides more flexibility and generally reads better. Both cannot
be used together.
<template name="_map-else_" desc="Non-matching map criteria">
<param name="@value@" desc="Constant to map to" />
<param name="@to@" desc="Named value (use instead of @value@)" />
<param name="@desc@" desc="Map description">
<text>Destination mapping</text>
</param>
<param name="@to-index@" desc="Named value index">
<text></text>
</param>
<param name="@_map-default@" desc="_map_-specified default">
<param-inherit meta="map_default" />
</param>
<!-- provide a more friendly error; otherwise, they'd get an error
from having two c:otherwise nodes, which would be confusing -->
<unless name="@_map-default@" eq="">
<error>
A default value was already provided by _map-set_/@default@
</error>
</unless>
<c:otherwise>
<if name="@value@">
<c:const value="@value@" desc="@desc@" />
</if>
<unless name="@value@">
<c:value-of name="@to@" index="@to-index@" />
</unless>
</c:otherwise>
</template>
</package>

33
core/numeric.xml 100644
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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"
core="true"
desc="Numeric computations">
<!-- we are a meta-package -->
<import package="numeric/boolean" export="true" />
<import package="numeric/convert" export="true" />
<import package="numeric/minmax" export="true" />
<import package="numeric/round" export="true" />
<import package="numeric/common" export="true" />
</package>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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"
core="true"
desc="Numeric computations dealing with boolean algebra">
<import package="../base" />
<function name="not" desc="Negates a boolean value" sym="\lnot">
<param name="not_value" type="boolean" desc="Boolean value to negate" />
<c:const value="1" type="boolean" desc="Value of 1 if given value is zero">
<c:when name="not_value">
<c:eq>
<c:const value="0" type="boolean" desc="Value to assert against for returning 1" />
</c:eq>
</c:when>
</c:const>
</function>
</package>

View File

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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="General numeric opreations">
<import package="../base" />
<section title="Stepping">
Due to the declarative nature of~TAME, recursive operations
needing to step are~not all that common. These operations are
useful when converting between 1-indexed and 0-indexed data.
\ref{_inc_} increments a value by a~single step (default~1).
<template name="_inc_"
desc="Increment value [by 1]">
<param name="@values@" desc="Value to increment (nodes)" />
<param name="@value@" desc="Value to decrement by (default 1)">
<text>1</text>
</param>
<c:sum>
<param-copy name="@values@" />
<c:const value="@value@"
desc="Increment by 1" />
</c:sum>
</template>
\ref{_dec_} decrements a value by a~single step (default~1).
<template name="_dec_"
desc="Decrement value [by 1]">
<param name="@values@" desc="Value to decrement (nodes)" />
<param name="@value@" desc="Value to decrement by (default 1)">
<text>1</text>
</param>
<c:sum>
<param-copy name="@values@" />
<t:negate>
<c:const value="@value@"
desc="Decrement by 1" />
</t:negate>
</c:sum>
</template>
</section>
<section title="Negation">
Negation is a common task and it was tedious in older versions of
TAME\footnote{Before it was even called TAME, actually.} For
these situations, \ref{NEGATE} is provided to avoid having to use
a~\ref{const} expression. To avoid a {\tt product} expression
altogether, use~\ref{_negate_}.
<const name="NEGATE" value="-1"
desc="Negate a value"
sym="-" />
<template name="_negate_"
desc="Negate an expression">
<param name="@values@" desc="Expression to negate" />
<param name="@label@" desc="Application label (default empty)">
<text></text>
</param>
<c:product label="@label@">
<c:value-of name="NEGATE" />
<param-copy name="@values@" />
</c:product>
</template>
</section>
<!-- Everything below this line must be moved. -->
<template name="_percent-of_" desc="Take percentage of a value">
<param name="@value@" desc="Value to take percentage of" />
<param name="@index@" desc="Index" />
<param name="@percent@" desc="Percent (as a whole number)" />
<param name="@desc@" desc="Optional description">
<text>Percent to reduce</text>
</param>
<c:product>
<c:value-of name="@value@" index="@index@" />
<t:ptor value="@percent@" desc="@desc@" />
</c:product>
</template>
<!--
Calculates a value based on the given multiplier if the given value falls
within the given range
-->
<function name="rangeadd" desc="Add a value multiplier for a given range">
<param name="low" type="float" desc="Lower bound, inclusive" />
<param name="high" type="float" desc="Upper bound, inclusive" />
<param name="val" type="float" desc="Value to compare" />
<param name="mult" type="float" desc="Range multiplier" />
<c:product>
<c:when name="val">
<c:gte>
<c:value-of name="low" />
</c:gte>
</c:when>
<c:when name="val">
<c:lte>
<c:value-of name="high" />
</c:lte>
</c:when>
<!-- atop of the 0.00003 we've already provided = 0.000035 -->
<c:value-of name="mult" />
<c:sum>
<c:value-of name="val" />
<!-- subtract -->
<c:product>
<c:value-of name="NEGATE" />
<c:value-of name="low" />
</c:product>
</c:sum>
</c:product>
</function>
</package>

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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="Converts scalars to another type">
<import package="../base" />
<!-- TODO: transitional; remove (used to contain _ptor_ and friends) -->
<import package="percent" export="true" />
<!-- for template applications -->
<import package="common" export="true" />
<import package="round" export="true" />
<!-- even more trivial, but again, cuts down on code -->
<template name="_scalarToAccum_" desc="Simply accumulates a scalar">
<param name="@scalar@" desc="Scalar to accumulate" />
<param name="@accum@" desc="Accumulator to accumulate into" />
<!-- this is useless, but required -->
<param name="@yields@" desc="Value to yield into, since it's required (useless)">
<text>__accum_</text>
<param-value name="@accum@" />
<text>_</text>
<param-value name="@scalar@" />
</param>
<param name="@type@" desc="Accumulation method">
<text>all</text>
</param>
<rate yields="@yields@">
<accumulate into="@accum@" type="@type@" />
<c:value-of name="@scalar@" />
</rate>
</template>
<!--
Map values falling within adjacent intervals
Interval endpoints are defined using `_endpoint_` nodes. The
first interval is defined as [-∞,@start@), and the last endpoint
as (end,∞], where `end` is the value of the final `_endpoint_`.
The default maps for these two implicit intervals default to 0,
but may be changed with, respectively, @low@ and @high@.
Permitted children:
- _endpoint_* - Endpoint definition.
-->
<template name="_map-interval_"
desc="Map adjacent, closed intervals to scalars">
<param name="@values@" desc="Interval definitions" />
<param name="@name@" desc="Reference value" />
<param name="@start@"
desc="Left endpoint of first interval (constant)" />
<param name="@index@"
desc="Reference value index">
<text></text>
</param>
<param name="@low@"
desc="Default value when less than @start@">
<text>#0</text>
</param>
<param name="@lowindex@"
desc="Low value index">
<text></text>
</param>
<param name="@high@"
desc="Default value when greater than final endpoint value">
<text>#0</text>
</param>
<param name="@highindex@"
desc="High value index">
<text></text>
</param>
<c:let>
<!-- c:when does not support shorthand constants (yet); this is
a workaround -->
<c:values>
<c:value name="__intval"
type="float"
desc="Interval reference value">
<c:value-of name="@name@" index="@index@" />
</c:value>
</c:values>
<c:cases>
<c:case>
<c:when name="__intval">
<c:lt>
<c:const value="@start@"
type="float"
desc="First interval left endpoint" />
</c:lt>
</c:when>
<c:value-of name="@low@" index="@lowindex@" />
</c:case>
<param-copy name="@values@" />
<c:otherwise>
<c:value-of name="@high@" index="@highindex@" />
</c:otherwise>
</c:cases>
</c:let>
</template>
<!--
Adjacent endpoint definition
A value that falls within range of the associated interval will be
mapped to the value @map@.
The first endpoint defined is the right endpoint of the interval
defined by [start,@value@], where `start` is defined by the parent
`_map-interval_`.
All other endpoints define the interval (prev,@value@].
-->
<template name="_endpoint_"
desc="Define an interval endpoint for _map-interval_">
<param name="@value@"
desc="Right endpoint of previous interval" />
<param name="@map@"
desc="Value to which interval is mapped" />
<param name="@mapindex@"
desc="Map value index">
<text></text>
</param>
<c:case>
<c:when name="__intval">
<c:lte>
<c:const value="@value@"
type="float"
desc="Previous interval right endpoint" />
</c:lte>
</c:when>
<c:value-of name="@map@"
index="@mapindex@" />
</c:case>
</template>
</package>

View File

@ -0,0 +1,344 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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="Numeric computations dealing with minimums and maximums">
<import package="../base" />
<function name="max" desc="Return the greater value">
<param name="max1" type="float" desc="First value to compare" />
<param name="max2" type="float" desc="Second value to compare" />
<c:cases>
<c:case>
<c:when name="max1">
<c:gte>
<c:value-of name="max2" />
</c:gte>
</c:when>
<c:value-of name="max1" />
</c:case>
<c:otherwise>
<c:value-of name="max2" />
</c:otherwise>
</c:cases>
</function>
<function name="min" desc="Return the lesser value">
<param name="min1" type="float" desc="First value to compare" />
<param name="min2" type="float" desc="Second value to compare" />
<c:cases>
<c:case>
<c:when name="min1">
<c:lte>
<c:value-of name="min2" />
</c:lte>
</c:when>
<c:value-of name="min1" />
</c:case>
<c:otherwise>
<c:value-of name="min2" />
</c:otherwise>
</c:cases>
</function>
<template name="_min-zero_" desc="Does not allow a value below 0 to be yielded">
<param name="@values@" desc="Calculation to apply restriction to" />
<param name="@label@" desc="Application label">
<!-- default empty -->
<text></text>
</param>
<c:apply name="max" label="@label@">
<c:arg name="max1">
<c:const value="0" type="integer" desc="Do not allow a value under 0" />
</c:arg>
<c:arg name="max2">
<param-copy name="@values@" />
</c:arg>
</c:apply>
</template>
<!-- trivial, but cuts turns a verbose rate-each and function application
into potentially a one-line template application -->
<template name="_quickMaxEach_" desc="Quick max application">
<param name="@class@" desc="Class to match on" />
<param name="@generates@" desc="Value to generate into" />
<param name="@a@" desc="Value a" />
<param name="@b@" desc="Value b" />
<param name="@yields@" desc="Yield variable">
<text>_</text>
<param-value name="@generates@" />
</param>
<rate-each class="@class@" accumulate="none" yields="@yields@" generates="@generates@" index="k">
<c:apply name="max">
<c:arg name="max1">
<c:value-of name="@a@" index="k" />
</c:arg>
<c:arg name="max2">
<c:value-of name="@b@" index="k" />
</c:arg>
</c:apply>
</rate-each>
</template>
<template name="_cap_"
desc="Cap a value at a given maximum">
<param name="@values@" desc="Value expression" />
<param name="@name@" desc="Value to cap at" />
<param name="@index@" desc="Index of value to cap at">
<text></text>
</param>
<param name="@value@"
desc="Constant value to cap at (deprecated)" />
<param name="@desc@" desc="Value description">
<text>Maximum value</text>
</param>
<c:apply name="min">
<c:arg name="min1">
<!-- deprecated -->
<if name="@value@">
<c:const value="@value@" type="float" desc="@desc@" />
</if>
<unless name="@value@">
<c:value-of name="@name@"
index="@index@"
type="float"
label="@desc@" />
</unless>
</c:arg>
<c:arg name="min2">
<param-copy name="@values@" />
</c:arg>
</c:apply>
</template>
<!-- useful in products -->
<template name="_min-value-of_" desc="Return a value or a minimum">
<param name="@name@" desc="Name of value" />
<param name="@index@" desc="Value index" />
<param name="@label@" desc="Optional label" />
<param name="@min@" desc="Minimum value" />
<c:apply name="max" label="{@label@}, minimum of 1">
<c:arg name="max1">
<c:const value="@min@" type="float" desc="Minimum value" />
</c:arg>
<c:arg name="max2">
<c:value-of name="@name@" index="@index@" label="@label@" />
</c:arg>
</c:apply>
</template>
<template name="_between_" desc="Assert that a value is within the given range, inclusive">
<param name="@min@" desc="Minimum value, inclusive" />
<param name="@max@" desc="Maximum value, inclusive" />
<param name="@desc@" desc="Description" />
<c:gte>
<c:const value="@min@" type="float" desc="{@desc@}; minimum" />
</c:gte>
<c:lte>
<c:const value="@max@" type="float" desc="{@desc@}; maximum" />
</c:lte>
</template>
<!--
Performs min and max bumping
TODO: max is incomplete (should have all the options of min)
-->
<template name="_bump_" desc="Bump a value if it does not reach a minimum">
<!-- if a minimum percentage -->
<param name="@percent@" desc="Percent, as a whole value" />
<param name="@value@" desc="Value to take percentage of, or take whole value of if no percent" />
<param name="@vindex@" desc="Optional index for base" />
<param name="@name@" desc="Provided value" />
<param name="@generates@" desc="Variable to generate into" />
<param name="@when@" desc="Conditional bump" />
<param name="@class@" desc="Class to match on" />
<param name="@keep@" desc="Value of keep flag" />
<!-- alternative to @name@ -->
<param name="@const@" desc="Constant value, instead of named" />
<!-- max values for bumping down -->
<param name="@maxpercent@" desc="Maximum percent" />
<rate yields="_{@generates@}" keep="@keep@">
<c:sum of="@name@" index="k" generates="@generates@" desc="Bumped value">
<c:cases>
<!-- if a condition was provided, check it first -->
<if name="@when@">
<c:case>
<c:when name="@when@" index="k">
<c:eq>
<c:value-of name="FALSE" />
</c:eq>
</c:when>
<!-- just return the value provided -->
<c:value-of name="@name@" index="k" />
</c:case>
</if>
<!-- if a condition was provided, check it first -->
<if name="@class@">
<c:case>
<c:when name="@class@" index="k">
<c:eq>
<c:value-of name="FALSE" />
</c:eq>
</c:when>
<!-- just return the value provided -->
<c:const value="0" type="float" desc="Zero value" />
</c:case>
</if>
<!-- condition was not provided or did not match (we check for the
negative of the condition above so that this template will still
produce valid output event if @when@ is not provided; the
c:cases will be optimized away in that case) -->
<c:otherwise>
<c:let>
<c:values>
<c:value name="min" type="float" desc="Minimum">
<unless name="@const@">
<!-- this will produce a percentage of the provided value, unless no
percent was given, in which case the entire value will be used
-->
<c:product>
<!-- if a percent was provided, then take a certain percentage of the value -->
<if name="@percent@">
<!-- given index or default index? -->
<if name="@vindex@">
<c:value-of name="@value@" index="@vindex@" />
</if>
<unless name="@vindex@">
<c:value-of name="@value@" index="k" />
</unless>
<c:quotient label="Percent as real number">
<c:const value="@percent@" type="integer" desc="Whole percent" />
<c:const value="100" type="integer" desc="Divisor to convert percent to real number" />
</c:quotient>
</if>
<!-- otherwise, use the given value -->
<unless name="@percent@">
<c:value-of name="@name@" index="k" />
</unless>
</c:product>
</unless>
<if name="@const@">
<c:const value="@const@" type="float" desc="Constant minimum value" />
</if>
</c:value>
</c:values>
<c:let>
<c:values>
<c:value name="minbumped" type="float" desc="Bumped to minimum">
<t:maxreduce>
<c:value-of name="min" />
<c:value-of name="@name@" index="k" />
</t:maxreduce>
</c:value>
<if name="@maxpercent@">
<c:value name="max" type="float" desc="Maximum">
<c:product>
<!-- given index or default index? -->
<if name="@vindex@">
<c:value-of name="@value@" index="@vindex@" />
</if>
<unless name="@vindex@">
<c:value-of name="@value@" index="k" />
</unless>
<c:quotient label="Max percent as real number">
<c:const value="@maxpercent@" type="integer" desc="Whole max percent" />
<c:const value="100" type="integer" desc="Divisor to convert max percent to real number" />
</c:quotient>
</c:product>
</c:value>
</if>
</c:values>
<c:cases>
<!-- if a max percent was provided, then check to ensure it
was not exceeded -->
<if name="@maxpercent@">
<c:case>
<c:when name="@name@" index="k">
<c:gt>
<c:value-of name="max" />
</c:gt>
</c:when>
<c:value-of name="max" />
</c:case>
</if>
<!-- if no max, just yield the bumped value according to the minimum -->
<c:otherwise>
<c:value-of name="minbumped" />
</c:otherwise>
</c:cases>
</c:let>
</c:let>
</c:otherwise>
</c:cases>
</c:sum>
</rate>
</template>
</package>

View File

@ -0,0 +1,367 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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="Percentage arithmetic">
<import package="../base" />
Converting between real and~percent is trivial:
$$
\begin{align}
r &amp;= p\over100;
p &amp;= 100r.
\end{align}
$$
^[Percent difference] (also called ^[relative change]) describes
the~change in value as a~percentage (for example: $10$~is $50%$
of~$20$, and is~$100%$ of~$5$). ^[Percent change] recognizes the
direction of the change (that is---$10$ is~$20$ reduced by~$50%$, so
$p=-50$).
Since ``change'' sounds like a~verb eclarative contexts, we use
the~term ^[percent difference] to mean ^[relative change]; it is
defined as:
$$\delta \over x = {{x_\beta - x_\alpha}\over{x_\alpha}} \times 100 = p.$$
Notice that this preserves the direction of the change. So, in the
context of the previous example, we can say that $10$
\emph{reduces}~$20$ by~$50%$, giving~$p=-50%$.
<template name="_rtop_"
desc="Rational number to percentage">
<param name="@name@" desc="Percentage to convert" />
<param name="@index@" desc="Index">
<text></text>
</param>
<param name="@desc@" desc="Optional description">
<text>Real number to convert into percentage</text>
</param>
<param name="@difference@"
desc="Whether to interpret as a percent difference (default
false)">
<text>false</text>
</param>
<param name="@negate@" desc="Whether to negate percentage before
conversion">
<text>false</text>
</param>
<c:product>
<if name="@negate@" eq="true">
<c:const value="-1" desc="Negate result" />
</if>
<c:sum>
<c:value-of name="@name@" index="@index@" />
<if name="@difference@" eq="true">
<c:const value="-1"
desc="Percent difference is relative (1 = 100%)" />
</if>
</c:sum>
<c:const value="100"
desc="Real to percentage" />
</c:product>
</template>
<template name="_ptor_" desc="Percent to rational number">
<param name="@name@" desc="Percentage to convert" />
<param name="@index@" desc="Index" />
<param name="@value@" desc="Constant (instead of named value)" />
<param name="@desc@" desc="Optional description for constant value">
<text>Percent to convert</text>
</param>
<param name="@difference@" desc="Whether to perform a percent
difference (default false)">
<text>false</text>
</param>
<param name="@negate@" desc="Whether to negate percentage before
conversion">
<text>false</text>
</param>
<c:sum>
<if name="@difference@" eq="true">
<c:const value="1"
desc="Perform percent difference" />
</if>
<c:quotient>
<c:product>
<if name="@negate@" eq="true">
<c:value-of name="NEGATE" />
</if>
<if name="@name@">
<c:value-of name="@name@" index="@index@" />
</if>
<unless name="@name@">
<c:const value="@value@" type="float" desc="@desc@" />
</unless>
</c:product>
<c:const value="100" type="integer" desc="Convert to rational number" />
</c:quotient>
</c:sum>
</template>
<template name="_ptor-each_"
desc="Each percent to real number">
<param name="@name@" desc="Percentage to convert" />
<param name="@generates@" desc="Value to generate into" />
<param name="@difference@"
desc="Whether to perform a percent difference">
<text>false</text>
</param>
<param name="@negate@" desc="Whether to negate percentage before
conversion">
<text>false</text>
</param>
<rate yields="_{@generates@}">
<c:sum of="@name@"
generates="@generates@" index="k"
desc="Real representation of {@name@}">
<t:ptor difference="@difference@" negate="@negate@"
name="@name@" index="k" />
</c:sum>
</rate>
</template>
<!--
Yields a total percent as an integer and rational number. The
integer is useful for displaying a total percent (e.g. for
credits/surcharges) and the rational number is useful for using
for multiplication in an equation.
Example:
<t:total-percent class="foo"
to_int="fooPercentTotal" to_rat="fooTotal"
index="k">
<with-param name="@values@">
<c:value-of name="fooA" index="k" />
<c:value-of name="fooB" index="k" />
</with-param>
</t:total-percent>
Let fooA = 5, fooB = -7
will yield:
- fooPercentTotal = -2
- fooTotal = 0.98
-->
<template name="_total-percent_"
desc="Generates a total percentage as an integer and a
rational number">
<!-- for use by @values@ -->
<param name="@index@" desc="Index variable" />
<param name="@class@" desc="Classification match" />
<param name="@to_int@" desc="Integer total to yield" />
<param name="@to_rat@" desc="Rational number to yield" />
<param name="@values@" desc="Value nodes" />
<param name="@yields_int@" desc="Yields for integer total">
<text>_</text>
<param-value name="@to_int@" />
</param>
<param name="@yields_rat@" desc="Yields for rational total">
<text>_</text>
<param-value name="@to_rat@" />
</param>
<!-- percent total (e.g. 7 for 7%) -->
<rate-each class="@class@" yields="@yields_int@"
generates="@to_int@" index="@index@">
<c:sum>
<param-copy name="@values@" />
</c:sum>
</rate-each>
<!-- as a rational number (to multiply by; e.g. 1.07 for 7%, 0.93 for -7%) -->
<rate-each class="@class@" yields="@yields_rat@"
generates="@to_rat@" index="k">
<c:sum>
<c:const value="1" desc="Relative percent" />
<t:ptor name="@to_int@" index="k" />
</c:sum>
</rate-each>
</template>
<!--
This template is useful for, say, when a coverage is partially
included, but additional coverage may be purchased. For example,
if 10% of coverage X is included, but you can purchase more Y if
10% is insufficient, then we can simply reduce coverage Y by 10%
of coverage X.
If the initial value is insufficient, then the value 0 will be
produced.
-->
<template name="_percent-reduction_"
desc="Reduces a set of values by a percentage of another set of values">
<param name="@index@" desc="Value index" />
<param name="@reduce@" desc="Vector to reduce values of" />
<param name="@percentof@" desc="Vector to reduce values by a percentage of" />
<param name="@percent@" desc="Percent reduction (as a float)" />
<param name="@minval@" desc="Minimum value to reduce by (optional)" />
<param name="@condition@" desc="Boolean conditions (vector for
each index) under which reduction should be applied" />
<param name="@desc@" desc="Optional description">
<text>Percent to reduce by</text>
</param>
<param name="@label@" desc="Label for max() application">
<text>Reduced </text>
<param-value name="@reduce@" />
<text> by </text>
<param-value name="@percent@" />
<text>*</text>
<param-value name="@percentof@" />
<text> (cannot fall below 0)</text>
</param>
<c:apply name="max" label="@label@">
<!-- if the reduction produces a value less than 0, then simply return 0 -->
<c:arg name="max1">
<c:const value="0"
desc="0 will be returned if the reduction is too great" />
</c:arg>
<c:arg name="max2">
<c:sum>
<c:value-of name="@reduce@" index="@index@"
label="Initial value" />
<c:let>
<c:values>
<c:value name="reduce" type="float"
desc="Value to reduce by, should condition be met">
<c:product>
<c:const value="@percent@" desc="@desc@" />
<c:value-of name="@percentof@" index="@index@" />
</c:product>
</c:value>
</c:values>
<t:negate>
<c:product label="Conditional reduction">
<if name="@condition@">
<c:when name="@condition@" index="@index@">
<c:eq>
<c:value-of name="TRUE" />
</c:eq>
</c:when>
</if>
<if name="@minval@">
<c:apply name="max" max1="reduce">
<c:arg name="max2">
<c:const value="@minval@"
desc="Minimum value to reduce by" />
</c:arg>
</c:apply>
</if>
<unless name="@minval@">
<c:value-of name="reduce" label="Value to reduce by" />
</unless>
</c:product>
</t:negate>
</c:let>
</c:sum>
</c:arg>
</c:apply>
</template>
<template name="_percent-of-set_"
desc="Returns the percent of a set of given values">
<param name="@class@" desc="Class match" />
<param name="@of@" desc="Vector of values to take the percent of" />
<param name="@percent@" desc="Percent reduction (as a float)" />
<param name="@generates@" desc="Value to generate" />
<param name="@yields@" desc="Yield">
<param-value name="@generates@" />
<text>Total</text>
</param>
<rate-each class="@class@" yields="@yields@"
generates="@generates@" index="k">
<c:apply name="round">
<c:arg name="roundval">
<c:apply name="max">
<!-- if the reduction produces a value less than 0, then simply return 0 -->
<c:arg name="max1">
<c:const value="0"
desc="0 will be returned if the reduction is too great" />
</c:arg>
<c:arg name="max2">
<c:product label="Percent value">
<c:const value="@percent@" desc="Percent to reduce by" />
<c:value-of name="@of@" index="k"
label="Value to take percentage of" />
</c:product>
</c:arg>
</c:apply>
</c:arg>
</c:apply>
</rate-each>
</template>
</package>

View File

@ -0,0 +1,264 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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="Numeric computations dealing with rounding">
<import package="../base" />
<!-- template version of round function, supporting arbitrary nodes -->
<template name="_round_" desc="Round to the nearest integer">
<param name="@values@" desc="Calculation to round (single node)" />
<param name="@precision@" desc="Level of precision after decimal point">
<text>0</text>
</param>
<c:apply name="round_real" label="Rounded value">
<c:arg name="round_real_val">
<c:sum>
<param-copy name="@values@" />
</c:sum>
</c:arg>
<c:arg name="round_real_n">
<c:expt>
<c:const value="10" type="integer" desc="Decimal base" />
<c:const value="@precision@" type="integer" desc="Exponent" />
</c:expt>
</c:arg>
</c:apply>
</template>
<!--
Let expression exposing one or more of ceiling, floor, and nearest
integer of given name
This is our Swiss Army knife of rounding values.
The values @high@, @low@, and @nearest@ (can specify all or just one)
represent the names of the variables to store, respectively, the
ceiling, floor, and nearest integer to the value @name@. Those values
are then accessible within the scope of the let expression.
An @exp@ can be used to provide a base-10 exponent to move the decimal
place (negative is right, positive left) before performing rounding.
The @step@ allows specifying an arbitrary value to round toward. For
example, a @step@ of 1500 rounds in 1500 increments. This may be used
with @exp@, in which case the step will apply to @name@ after its value
has been exponentiated.
-->
<template name="_let-round_"
desc="Produce floor and ceiling values">
<param name="@values@"
desc="Calculation subject to let expression" />
<param name="@name@"
desc="Value to round" />
<param name="@index@"
desc="Optional value index">
<text></text>
</param>
<param name="@high@"
desc="Name to yield ceil'd value into" />
<param name="@low@"
desc="Name to yield floor'd value into" />
<param name="@nearest@"
desc="Name to yield nearest integer to value into" />
<param name="@exp@"
desc="Offset digits before rounding">
<text>#0</text>
</param>
<param name="@step@"
desc="Arbitrary step">
<text>#1</text>
</param>
<c:let>
<c:values>
<c:value name="__div"
type="float"
desc="Exponential/step divisor">
<c:product>
<c:expt>
<c:const value="10" type="integer"
desc="Decimal base" />
<c:value-of name="@exp@" />
</c:expt>
<c:value-of name="@step@" />
</c:product>
</c:value>
</c:values>
<c:let>
<c:values>
<c:value name="__adjusted"
type="float"
desc="Value adjusted for 10^@exp@ or step @step@">
<c:quotient>
<c:value-of name="@name@" index="@index@" />
<c:value-of name="__div" />
</c:quotient>
</c:value>
</c:values>
<c:let>
<c:values>
<if name="@high@">
<c:value name="@high@" type="float"
desc="Ceiling of adjusted {@name@}">
<c:product>
<c:ceil>
<c:value-of name="__adjusted" />
</c:ceil>
<c:value-of name="__div" />
</c:product>
</c:value>
</if>
<if name="@low@">
<c:value name="@low@" type="float"
desc="floor of adjusted {@name@}">
<c:product>
<c:floor>
<c:value-of name="__adjusted" />
</c:floor>
<c:value-of name="__div" />
</c:product>
</c:value>
</if>
<if name="@nearest@">
<c:value name="@nearest@" type="float"
desc="nearest integer to adjusted {@name@}">
<c:product>
<t:round>
<c:value-of name="__adjusted" />
</t:round>
<c:value-of name="__div" />
</c:product>
</c:value>
</if>
</c:values>
<!-- body subject to let expression -->
<param-copy name="@values@" />
</c:let>
</c:let>
</c:let>
</template>
<!-- ceil if > 0.5, otherwise floor; round() in most languages -->
<!-- TODO: support left and right delimiters; do \floor \ceil -->
<function name="round" desc="Round to the nearest integer">
<param name="roundval" type="float" desc="Value to round to nearest integer" />
<!-- By raising by 0.5 and taking the floor() of the resulting value, we
can produce values consistent with common round() implementations. It
may be easier to think of this as ceil( val - 0.4 ), but representing
subtraction is more verbose than addition, so a floor() implementation
is used instead. -->
<c:floor>
<c:sum>
<c:value-of name="roundval" />
<c:const value="0.5" type="float" desc="Raises value in a manner that it can be properly rounded by a floor" />
</c:sum>
</c:floor>
</function>
<function name="round_real" desc="Round to the nearest 1/n">
<param name="round_real_val" type="float" desc="Value to round" />
<param name="round_real_n" type="integer" desc="Round to 1/n" />
<!-- we can achieve this by multiplying by N, rounding, and then dividing
by the same N -->
<c:quotient>
<c:apply name="round">
<c:arg name="roundval">
<c:product>
<c:value-of name="round_real_val" />
<c:value-of name="round_real_n" />
</c:product>
</c:arg>
</c:apply>
<c:value-of name="round_real_n" />
</c:quotient>
</function>
<!-- this is simply a shorthand for round_real -->
<function name="round_cents" desc="Round to the nearest penny">
<param name="round_cents_val" type="float" desc="Monetary value" />
<c:apply name="round_real">
<c:arg name="round_real_n">
<c:const value="100" type="integer" desc="Round to the nearest 100th" />
</c:arg>
<c:arg name="round_real_val">
<c:value-of name="round_cents_val" />
</c:arg>
</c:apply>
</function>
<template name="_ceil-n_" desc="Ceiling on the last n digits">
<param name="@values@" desc="Calculation to use (single node)" />
<param name="@digits@" desc="Level of precision after decimal point" />
<c:product>
<c:ceil>
<c:quotient>
<param-copy name="@values@" />
<c:expt>
<c:const value="10" type="integer" desc="Decimal base" />
<c:const value="@digits@" type="integer" desc="Number of digits" />
</c:expt>
</c:quotient>
</c:ceil>
<c:expt>
<c:const value="10" type="integer" desc="Decimal base" />
<c:const value="@digits@" type="integer" desc="Number of digits" />
</c:expt>
</c:product>
</template>
</package>

195
core/state.xml 100644
View File

@ -0,0 +1,195 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2017, 2018 R-T Specialty, 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="United States">
<import package="base" />
<import package="vector/stub" />
<section title="States">
The type \ref{State} contains each of the States in the United~States,
with the addition of Washington~DC.
The States are 1-indexed and sorted by \emph{abbreviation}\footnote{
States are sorted by abbreviation rather than the State name beacuse
they are most frequently referenced as such.}.
<typedef name="State" desc="States">
<enum type="integer">
<item name="STATE_NONE" value="0" desc="No State" />
<item name="STATE_AK" value="1" desc="Alaska" />
<item name="STATE_AL" value="2" desc="Alabama" />
<item name="STATE_AR" value="3" desc="Arkansas" />
<item name="STATE_AZ" value="4" desc="Arizona" />
<item name="STATE_CA" value="5" desc="California" />
<item name="STATE_CO" value="6" desc="Colorado" />
<item name="STATE_CT" value="7" desc="Connecticut" />
<item name="STATE_DC" value="8" desc="Washington DC" />
<item name="STATE_DE" value="9" desc="Delaware" />
<item name="STATE_FL" value="10" desc="Florida" />
<item name="STATE_GA" value="11" desc="Georgia" />
<item name="STATE_HI" value="12" desc="Hawaii" />
<item name="STATE_IA" value="13" desc="Iowa" />
<item name="STATE_ID" value="14" desc="Idaho" />
<item name="STATE_IL" value="15" desc="Illinois" />
<item name="STATE_IN" value="16" desc="Indiana" />
<item name="STATE_KS" value="17" desc="Kansas" />
<item name="STATE_KY" value="18" desc="Kentucky" />
<item name="STATE_LA" value="19" desc="Louisiana" />
<item name="STATE_MA" value="20" desc="Massachusetts" />
<item name="STATE_MD" value="21" desc="Maryland" />
<item name="STATE_ME" value="22" desc="Maine" />
<item name="STATE_MI" value="23" desc="Michigan" />
<item name="STATE_MN" value="24" desc="Minnesota" />
<item name="STATE_MO" value="25" desc="Missouri" />
<item name="STATE_MS" value="26" desc="Mississippi" />
<item name="STATE_MT" value="27" desc="Montana" />
<item name="STATE_NC" value="28" desc="North Carolina" />
<item name="STATE_ND" value="29" desc="North Dakota" />
<item name="STATE_NE" value="30" desc="Nebraska" />
<item name="STATE_NH" value="31" desc="New Hampshire" />
<item name="STATE_NJ" value="32" desc="New Jersey" />
<item name="STATE_NM" value="33" desc="New Mexico" />
<item name="STATE_NV" value="34" desc="Nevada" />
<item name="STATE_NY" value="35" desc="New York" />
<item name="STATE_OH" value="36" desc="Ohio" />
<item name="STATE_OK" value="37" desc="Oklahoma" />
<item name="STATE_OR" value="38" desc="Oregon" />
<item name="STATE_PA" value="39" desc="Pennsylvania" />
<item name="STATE_RI" value="40" desc="Rhode Island" />
<item name="STATE_SC" value="41" desc="South Carolina" />
<item name="STATE_SD" value="42" desc="South Dakota" />
<item name="STATE_TN" value="43" desc="Tennessee" />
<item name="STATE_TX" value="44" desc="Texas" />
<item name="STATE_UT" value="45" desc="Utah" />
<item name="STATE_VA" value="46" desc="Virginia" />
<item name="STATE_VT" value="47" desc="Vermont" />
<item name="STATE_WA" value="48" desc="Washington" />
<item name="STATE_WI" value="49" desc="Wisconsin" />
<item name="STATE_WV" value="50" desc="West Virginia" />
<item name="STATE_WY" value="51" desc="Wyoming" />
</enum>
</typedef>
</section>
<section title="State Operations">
For operations that involve taking values of all states where order
matters (e.g. for index alignment),
querying the symbol table isn't appropriate,
as it does not guarantee order.
\ref{_for-each-state_} can be used for that purpose;
it exposes the following template values to its body:
\begin{enumerate}
\item \tt{@state_const@} contains the State constant;
\item \tt{@state_upper@} contains the uppercase two-letter State
abbreviation;
\item \tt{@state_lower@} contains the lowercase two-letter State
abbreviation; and
\item \tt{@state_name@} contains the full state name.
\end{enumerate}
This can be used to generate a ^[State vector] by mapping an iteration
index to the State constant~\tt{@state_const@}.
Note that \ref{STATE_NONE} is not in the list.
<template name="_for-each-state_"
desc="Apply body for each State">
<param name="@values@" desc="Body to apply per State" />
<inline-template>
<for-each>
<set state_const="STATE_AK" state_upper="AK" state_lower="ak" state_name="Alaska" />
<set state_const="STATE_AL" state_upper="AL" state_lower="al" state_name="Alabama" />
<set state_const="STATE_AR" state_upper="AR" state_lower="ar" state_name="Arkansas" />
<set state_const="STATE_AZ" state_upper="AZ" state_lower="az" state_name="Arizona" />
<set state_const="STATE_CA" state_upper="CA" state_lower="ca" state_name="California" />
<set state_const="STATE_CO" state_upper="CO" state_lower="co" state_name="Colorado" />
<set state_const="STATE_CT" state_upper="CT" state_lower="ct" state_name="Connecticut" />
<set state_const="STATE_DC" state_upper="DC" state_lower="dc" state_name="Washington DC" />
<set state_const="STATE_DE" state_upper="DE" state_lower="de" state_name="Delaware" />
<set state_const="STATE_FL" state_upper="FL" state_lower="fl" state_name="Florida" />
<set state_const="STATE_GA" state_upper="GA" state_lower="ga" state_name="Georgia" />
<set state_const="STATE_HI" state_upper="HI" state_lower="hi" state_name="Hawaii" />
<set state_const="STATE_IA" state_upper="IA" state_lower="ia" state_name="Iowa" />
<set state_const="STATE_ID" state_upper="ID" state_lower="id" state_name="Idaho" />
<set state_const="STATE_IL" state_upper="IL" state_lower="il" state_name="Illinois" />
<set state_const="STATE_IN" state_upper="IN" state_lower="in" state_name="Indiana" />
<set state_const="STATE_KS" state_upper="KS" state_lower="ks" state_name="Kansas" />
<set state_const="STATE_KY" state_upper="KY" state_lower="ky" state_name="Kentucky" />
<set state_const="STATE_LA" state_upper="LA" state_lower="la" state_name="Louisiana" />
<set state_const="STATE_MA" state_upper="MA" state_lower="ma" state_name="Massachusetts" />
<set state_const="STATE_MD" state_upper="MD" state_lower="md" state_name="Maryland" />
<set state_const="STATE_ME" state_upper="ME" state_lower="me" state_name="Maine" />
<set state_const="STATE_MI" state_upper="MI" state_lower="mi" state_name="Michigan" />
<set state_const="STATE_MN" state_upper="MN" state_lower="mn" state_name="Minnesota" />
<set state_const="STATE_MO" state_upper="MO" state_lower="mo" state_name="Missouri" />
<set state_const="STATE_MS" state_upper="MS" state_lower="ms" state_name="Mississippi" />
<set state_const="STATE_MT" state_upper="MT" state_lower="mt" state_name="Montana" />
<set state_const="STATE_NC" state_upper="NC" state_lower="nc" state_name="North Carolina" />
<set state_const="STATE_ND" state_upper="ND" state_lower="nd" state_name="North Dakota" />
<set state_const="STATE_NE" state_upper="NE" state_lower="ne" state_name="Nebraska" />
<set state_const="STATE_NH" state_upper="NH" state_lower="nh" state_name="New Hampshire" />
<set state_const="STATE_NJ" state_upper="NJ" state_lower="nj" state_name="New Jersey" />
<set state_const="STATE_NM" state_upper="NM" state_lower="nm" state_name="New Mexico" />
<set state_const="STATE_NV" state_upper="NV" state_lower="nv" state_name="Nevada" />
<set state_const="STATE_NY" state_upper="NY" state_lower="ny" state_name="New York" />
<set state_const="STATE_OH" state_upper="OH" state_lower="oh" state_name="Ohio" />
<set state_const="STATE_OK" state_upper="OK" state_lower="ok" state_name="Oklahoma" />
<set state_const="STATE_OR" state_upper="OR" state_lower="or" state_name="Oregon" />
<set state_const="STATE_PA" state_upper="PA" state_lower="pa" state_name="Pennsylvania" />
<set state_const="STATE_RI" state_upper="RI" state_lower="ri" state_name="Rhode Island" />
<set state_const="STATE_SC" state_upper="SC" state_lower="sc" state_name="South Carolina" />
<set state_const="STATE_SD" state_upper="SD" state_lower="sd" state_name="South Dakota" />
<set state_const="STATE_TN" state_upper="TN" state_lower="tn" state_name="Tennessee" />
<set state_const="STATE_TX" state_upper="TX" state_lower="tx" state_name="Texas" />
<set state_const="STATE_UT" state_upper="UT" state_lower="ut" state_name="Utah" />
<set state_const="STATE_VA" state_upper="VA" state_lower="va" state_name="Virginia" />
<set state_const="STATE_VT" state_upper="VT" state_lower="vt" state_name="Vermont" />
<set state_const="STATE_WA" state_upper="WA" state_lower="wa" state_name="Washington" />
<set state_const="STATE_WI" state_upper="WI" state_lower="wi" state_name="Wisconsin" />
<set state_const="STATE_WV" state_upper="WV" state_lower="wv" state_name="West Virginia" />
<set state_const="STATE_WY" state_upper="WY" state_lower="wy" state_name="Wyoming" />
</for-each>
<param-copy name="@values@" />
</inline-template>
</template>
To ease iteration though ^[State vector]s generated with
\ref{_for-each-state_},
a 52~vector \ref{NVEC_STATE_ALL} and classification \ref{state-all}
are provided.
<t:n-vector n="52" name="NVEC_STATE_ALL" value="1" />
<classify as="state-all" yields="stateAll"
desc="Every State in a State vector">
<match on="NVEC_STATE_ALL" />
</classify>
</section>
</package>

1
core/states.xml 120000
View File

@ -0,0 +1 @@
state.xml

92
core/symbol.xml 100644
View File

@ -0,0 +1,92 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="Symbol Introspection">
TAME provides powerful symbol table introspection features to assist
with metaprogramming.
This package provides templates to abstract some of those features and
make them easier to use.
If introspecting on symbols that are defined within the same package,
be aware that the symbols may not be available until a future pass;
\tt{expand-sequence} may be helpful in that situation.
\ref{_if-symbol_} is a general-purpose template to conditionally expand a
body if a symbol matches a given predicate.
The predicates currently supported are \tt{type} and \tt{dim}.
If no predicate is provided,
then the body will be expanded if the symbol exists.\footnote{
This is equivalent to \tt{type=""}.}
<template name="_if-symbol_"
desc="Expand body if symbol predicate matches">
<param name="@values@" desc="Condition body" />
<param name="@name@" desc="Symbol name" />
<param name="@type@" desc="Symbol type predicate" />
<param name="@dim@" desc="Symbol dimensions predicate" />
<param name="@_sym_type@" desc="Symbol type lookup">
<param-sym-value name="@name@" value="type" ignore-missing="true" />
</param>
<param name="@_sym_dim@" desc="Symbol dimensions lookup">
<param-sym-value name="@name@" value="dim" ignore-missing="true" />
</param>
<if name="@type@">
<if name="@_sym_type@" eq="@type@">
<param-copy name="@values@" />
</if>
</if>
<unless name="@type@">
<if name="@dim@">
<if name="@_sym_dim@" eq="@dim@">
<param-copy name="@values@" />
</if>
</if>
<!-- default simply checks to see if the symbol exists -->
<unless name="@dim@">
<unless name="@_sym_type@" eq="">
<param-copy name="@values@" />
</unless>
</unless>
</unless>
</template>
\ref{_if-defined_} is the same as \ref{_if-symbol_} with no predicates;
it provides more comfortable terminology for a common use case.
<template name="_if-defined_"
desc="Expand body if symbol is defined">
<param name="@values@" desc="Condition body" />
<param name="@name@" desc="Symbol name" />
<t:if-symbol name="@name@">
<param-copy name="@values@" />
</t:if-symbol>
</template>
</package>

44
core/tdat.xml 100644
View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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"
core="true"
desc="Territory data support (used in conjunction with tdat script)">
<import package="base" export="true" />
<template name="_terr-code_" desc="Defines a territory code for a given classification">
<param name="@class@" desc="Classification" />
<param name="@code@" desc="Territory code" />
<param name="@generates@" desc="Generator" />
<param name="@yields@" desc="Yield var">
<text>_</text>
<param-value name="@generates@" />
</param>
<rate-each class="@class@" accumulate="none" yields="@yields@" generates="@generates@" index="k">
<c:const value="@code@" type="integer" desc="Territory code" />
</rate-each>
</template>
</package>

View File

@ -0,0 +1,264 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="Aggregate Package Specification">
<import package="../../base" />
<import package="../../test/spec" />
<import package="../../base" />
<import package="../../vector/cmatch" />
<import package="../../vector/stub" />
<import package="../../aggregate" />
<rate-each class="nclass3"
generates="aggregateGen1" index="k">
<c:const value="1" desc="Constant value" />
</rate-each>
<rate-each class="nclass3"
generates="aggregateGen2" index="k">
<c:value-of name="k" />
</rate-each>
<rate yields="aggregateRate1">
<c:const value="1" desc="Constant value" />
</rate>
<rate yields="aggregateRate2">
<c:const value="3" desc="Constant value" />
</rate>
<classify as="agg-class-1"
desc="Aggregate test 1">
<match on="AGG_1VEC" />
</classify>
<classify as="agg-class-2"
desc="Aggregate test 2">
<t:match-gt on="AGG_INCVEC" const="0" />
</classify>
<t:n-vector n="3" name="AGG_1VEC" value="1" />
<const name="AGG_INCVEC" desc="Incrementing vector">
<item value="0" />
<item value="1" />
<item value="2" />
</const>
<t:describe name="aggregate template">
<t:describe name="_aggregate-rate-each_">
<t:aggregate-rate-each class="nclass3" yields="yieldAggReEmpty"
prefix="doesNotExist"
generates="genAggReEmpty" />
<t:aggregate-rate-each class="nclass3" yields="yieldAggReNonEmpty"
prefix="aggregateGen"
generates="genAggReNonEmpty" />
<t:describe name="with no symbols">
<t:it desc="produces 0">
<t:given>
<c:sum>
<c:value-of name="yieldAggReEmpty" />
<c:sum of="genAggReEmpty" />
</c:sum>
</t:given>
<t:expect>
<t:match-result eq="0" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with symbols">
<t:it desc="sums respective index of each symbol">
<t:given>
<c:sum of="genAggReNonEmpty" />
</t:given>
<t:expect>
<!-- 1 + 2 + 3 -->
<t:match-result eq="6" />
</t:expect>
</t:it>
<t:it desc="yields sum of symbols">
<t:given>
<c:value-of name="yieldAggReNonEmpty" />
</t:given>
<t:expect>
<!-- same as above -->
<t:match-result eq="6" />
</t:expect>
</t:it>
</t:describe>
</t:describe>
<t:describe name="_aggregate-rate_">
<t:aggregate-rate prefix="doesNotExist" yields="yieldAggRateEmpty" />
<t:aggregate-rate prefix="aggregateRate" yields="yieldAggRateNonEmpty" />
<t:describe name="with no symbols">
<t:it desc="yields 0">
<t:given>
<c:value-of name="yieldAggRateEmpty" />
</t:given>
<t:expect>
<t:match-result eq="0" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with symbols">
<t:it desc="yields sum of symbols">
<t:given>
<c:value-of name="yieldAggRateNonEmpty" />
</t:given>
<t:expect>
<t:match-result eq="4" />
</t:expect>
</t:it>
</t:describe>
</t:describe>
<t:describe name="_aggregate-classify_">
<t:describe name="as a univiersal quantifier">
<t:aggregate-classify prefix="does-not-exist" as="class-agg-univ-empty"
desc="Aggregate universal class empty test"
yields="classAggUnivEmpty" />
<t:aggregate-classify prefix="agg-class-" as="class-agg-univ-nonempty"
desc="Aggregate class nonempty test"
yields="classAggUnivNonEmpty" />
<t:describe name="with no symbols">
<t:it desc="produces scalar 1">
<t:given>
<c:value-of name="classAggUnivEmpty" />
</t:given>
<t:expect>
<t:match-result eq="1" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with symbols">
<t:it desc="generates matching class">
<rate-each class="class-agg-univ-nonempty"
yields="aggUnivNonEmptyCheck"
index="k">
<c:const value="1" desc="Truth check" />
</rate-each>
<t:expect>
<!-- two non-zero in AGG_INCVEC -->
<t:match-eq on="aggUnivNonEmptyCheck" const="2" />
</t:expect>
</t:it>
<t:it desc="produces vector">
<t:given>
<c:sum of="classAggUnivNonEmpty" />
</t:given>
<t:expect>
<!-- two non-zero in AGG_INCVEC -->
<t:match-result eq="2" />
</t:expect>
</t:it>
</t:describe>
</t:describe>
<t:describe name="as a existential quantifier">
<t:aggregate-classify prefix="does-not-exist" as="class-agg-exist-empty"
desc="Aggregate existersal class empty test"
yields="classAggExistEmpty"
any="true" />
<t:aggregate-classify prefix="agg-class-" as="class-agg-exist-nonempty"
desc="Aggregate class nonempty test"
yields="classAggExistNonEmpty"
any="true" />
<t:describe name="with no symbols">
<t:it desc="produces scalar 0">
<t:given>
<c:value-of name="classAggExistEmpty" />
</t:given>
<t:expect>
<t:match-result eq="0" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with symbols">
<t:it desc="generates matching class">
<rate-each class="class-agg-exist-nonempty"
yields="aggExistNonEmptyCheck"
index="k">
<c:const value="1" desc="Truth check" />
</rate-each>
<t:expect>
<!-- all match in AGG_1VEC -->
<t:match-eq on="aggExistNonEmptyCheck" const="3" />
</t:expect>
</t:it>
<t:it desc="produces vector">
<t:given>
<c:sum of="classAggExistNonEmpty" />
</t:given>
<t:expect>
<!-- all match in AGG_1VEC -->
<t:match-result eq="3" />
</t:expect>
</t:it>
</t:describe>
</t:describe>
</t:describe>
</t:describe>
</package>

View File

@ -0,0 +1,208 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2016 R-T Specialty, 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"
title="Test Insurance Abstractions">
<import package="../spec" />
<import package="../../base" />
<import package="../../dummy" />
<import package="../../vector/cmatch" />
<!-- SUT -->
<import package="../../insurance" />
<t:describe name="_premium_">
<t:describe name="@round@">
<t:it desc="Performs no round when 'none'">
<t:premium class="length1"
generates="premRoundNone" index="k"
round="none">
<c:value-of name="#0.5" />
</t:premium>
<t:given name="premRoundNone" />
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#0.5" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="Rounds to nearest integer when 'dollar' (up)">
<t:premium class="length1"
generates="premRoundDollarUp" index="k"
round="dollar">
<c:value-of name="#1.5" />
</t:premium>
<t:given name="premRoundDollarUp" />
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#2" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="Rounds to nearest integer when 'dollar' (down)">
<t:premium class="length1"
generates="premRoundDollarDown" index="k"
round="dollar">
<c:value-of name="#1.4" />
</t:premium>
<t:given name="premRoundDollarDown" />
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#1" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="Rounds to nearest cent when 'cent' (up)">
<t:premium class="length1"
generates="premRoundCentUp" index="k"
round="cent">
<c:value-of name="#1.505" />
</t:premium>
<t:given name="premRoundCentUp" />
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#1.51" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="Rounds to nearest cent when 'cent' (down)">
<t:premium class="length1"
generates="premRoundCentDown" index="k"
round="cent">
<c:value-of name="#1.504" />
</t:premium>
<t:given name="premRoundCentDown" />
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#1.50" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="Performs ceiling when 'up' (low)">
<t:premium class="length1"
generates="premRoundCeilLow" index="k"
round="up">
<c:value-of name="#1.1" />
</t:premium>
<t:given name="premRoundCeilLow" />
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#2" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="Performs ceiling when 'up' (high)">
<t:premium class="length1"
generates="premRoundCeilHigh" index="k"
round="up">
<c:value-of name="#1.7" />
</t:premium>
<t:given name="premRoundCeilHigh" />
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#2" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="Performs floor when 'down' (low)">
<t:premium class="length1"
generates="premRoundFloorLow" index="k"
round="down">
<c:value-of name="#1.1" />
</t:premium>
<t:given name="premRoundFloorLow" />
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#1" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="Performs floor when 'down' (high)">
<t:premium class="length1"
generates="premRoundFloorHigh" index="k"
round="down">
<c:value-of name="#1.7" />
</t:premium>
<t:given name="premRoundFloorHigh" />
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#1" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
</t:describe>
</package>

View File

@ -0,0 +1,209 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2015 R-T Specialty, 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="Test numeric computations dealing with rounding">
<import package="../../spec" />
<import package="../../../base" />
<import package="../../../numeric/convert" />
<t:describe name="_map-interval_">
<t:describe name="with values between intervals">
<t:it desc="maps start endpoint to first interval">
<t:given>
<!-- same value as start -->
<t:map-interval name="#5"
start="5">
<t:endpoint value="10" map="#1" />
<t:endpoint value="20" map="#2" />
</t:map-interval>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#1" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="maps first endpoint to first interval">
<t:given>
<!-- value same as first endpoint -->
<t:map-interval name="#10"
start="0">
<t:endpoint value="10" map="#1" />
<t:endpoint value="20" map="#2" />
</t:map-interval>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#1" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="maps value between start and first to first interval">
<t:given>
<!-- between start and first endpoint -->
<t:map-interval name="#5"
start="0">
<t:endpoint value="10" map="#1" />
<t:endpoint value="20" map="#2" />
</t:map-interval>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#1" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="maps value between first and second endpoint to second
interval">
<t:given>
<!-- between start and first endpoint -->
<t:map-interval name="#11"
start="0">
<t:endpoint value="10" map="#1" />
<t:endpoint value="20" map="#2" />
</t:map-interval>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#2" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="maps value after final endpoint to default">
<t:given>
<!-- between start and first endpoint -->
<t:map-interval name="#11"
start="0">
<t:endpoint value="10" map="#1" />
<t:endpoint value="20" map="#2" />
</t:map-interval>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#2" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<t:describe name="in interval [-∞,start) ">
<t:it desc="defaults to map value of 0">
<t:given>
<t:map-interval name="#1"
start="5" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#0" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="maps to low value when provided">
<t:given>
<t:map-interval name="#1"
start="5"
low="#2" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#2" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<t:describe name="in interval (end,∞] ">
<t:it desc="defaults to map value of 0">
<t:given>
<t:map-interval name="#20"
start="0">
<t:endpoint value="10" map="#1" />
</t:map-interval>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#0" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="maps to high value when provided">
<t:given>
<t:map-interval name="#20"
start="0"
high="#25">
<t:endpoint value="10" map="#1" />
</t:map-interval>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#25" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
</t:describe>
</package>

View File

@ -0,0 +1,178 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2015 R-T Specialty, 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="Test percentage arithemetic">
<import package="../../spec" />
<import package="../../../base" />
<import package="../../../numeric/percent" />
<t:describe name="_rtop_">
<t:describe name="given only a percentage">
<t:it desc="converts real into whole percentage">
<t:given>
<t:rtop name="#0.05" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#5" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="converts integer into whole percentage">
<t:given>
<t:rtop name="#5" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#500" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="converts negative into negative percentage">
<t:given>
<t:rtop name="#-0.5" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#-50" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<t:describe name="given a percent difference">
These tests are similar to the above, except that they represent
the~percent difference between two values. That is, given the
values $5$~and $15$, we arrive at a~$200%$ increase. So
if~$p=200$, then we want $5r=15, r=3=\((15-5)/5\)+1$.
<t:it desc="converts R into percentage P that will increase N
by P%">
<t:given>
<t:rtop name="#3" difference="true" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#200" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
Similarly, if we go from~$10$ to~$5$, we have~$p=-100$
and~$10r=5$; then~$r=1/2=0.5$.
<t:it desc="converts negative N into percentage P that will
decrease N by P%">
<t:given>
<t:rtop name="#0.5" difference="true" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#-50" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<t:describe name="given a value index">
<const name="PERCENT_REAL_VECTOR"
desc="Test vector">
<item value="3" desc="Real number" />
</const>
<t:it desc="converts the scalar value at the given index">
<t:given>
<t:rtop name="PERCENT_REAL_VECTOR" index="#0" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#300" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<t:describe name="given @negative@">
\ref{_rtop_} provides a way to negate the result using a
short-hand form for convenience and conciseness.
<t:it desc="negates resulting percentage">
<t:given>
<t:rtop name="#0.05" negate="true" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#-5" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:it desc="negates resulting difference">
<t:given>
<t:rtop name="#2" negate="true"
difference="true" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#-100" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
</t:describe>
</package>

View File

@ -0,0 +1,315 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2015, 2018 R-T Specialty, 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="Test numeric computations dealing with rounding">
<import package="../../../test/spec" />
<import package="../../../base" />
<import package="../../../numeric/round" />
<const name="VALUE_LOW" sym="\check{v}"
value="5.1" type="float"
desc="Low-decimal value" />
<const name="VALUE_MID" sym="\bar{v}"
value="5.5" type="float"
desc="High-decimal value" />
<const name="VALUE_HIGH" sym="\hat{v}"
value="5.7" type="float"
desc="High-decimal value" />
<const name="VALUE_FLOOR" sym="\lfloor v\rfloor"
value="5" type="float"
desc="Floor of decimal value" />
<const name="VALUE_CEIL" sym="\lceil v\rceil"
value="6" type="float"
desc="Ceiling of decimal value" />
<!-- used to test @index; same constant value as above -->
<const name="VALUE_VEC" sym="V"
type="float"
desc="Vector of values">
<item value="0" />
<item value="5.5" desc="Same as VALUE_MID" />
</const>
<const name="VALUE_VEC_INDEX" sym="\nu"
value="1" type="integer"
desc="Index of test value within vector" />
<const name="VALUE_LARGE" sym="\Omega"
value="1234.56" type="float"
desc="Value to test rounding offsets" />
<const name="VALUE_STEP" sym="s"
value="43.46" type="integer"
desc="Test arbitrary step" />
<!--
Eliminate let-round repetition for initial expectations
-->
<template name="_expect-let-round_"
desc="Short-hand rounding expectation">
<param name="@name@" desc="Value to round" />
<!-- choose *one* -->
<param name="@high@" desc="Round high" />
<param name="@low@" desc="Round high" />
<param name="@nearest@" desc="Round near" />
<!-- take whatever we're given and use that as the expected value -->
<param name="@__expect@" desc="Expected value">
<param-value name="@high@" />
<param-value name="@low@" />
<param-value name="@nearest@" />
</param>
<!-- round based on the provided template param -->
<t:given>
<if name="@high@">
<t:let-round name="@name@" high="rounded_high">
<c:value-of name="rounded_high" />
</t:let-round>
</if>
<if name="@low@">
<t:let-round name="@name@" low="rounded_low">
<c:value-of name="rounded_low" />
</t:let-round>
</if>
<if name="@nearest@">
<t:let-round name="@name@" nearest="rounded_nearest">
<c:value-of name="rounded_nearest" />
</t:let-round>
</if>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="@__expect@" />
</c:eq>
</t:match-result>
</t:expect>
</template>
<t:describe name="_let-round_">
<t:describe name="high">
<t:it desc="rounds up low decimals">
<t:expect-let-round name="VALUE_LOW"
high="VALUE_CEIL" />
</t:it>
<t:it desc="rounds up high decimals">
<t:expect-let-round name="VALUE_HIGH"
high="VALUE_CEIL" />
</t:it>
<t:it desc="rounds up halves">
<t:expect-let-round name="VALUE_MID"
high="VALUE_CEIL" />
</t:it>
<t:it desc="leaves integers unchanged">
<t:expect-let-round name="VALUE_FLOOR"
high="VALUE_FLOOR" />
<t:expect-let-round name="VALUE_CEIL"
high="VALUE_CEIL" />
</t:it>
</t:describe>
<t:describe name="low">
<t:it desc="rounds down low decimals">
<t:expect-let-round name="VALUE_LOW"
low="VALUE_FLOOR" />
</t:it>
<t:it desc="rounds down high decimals">
<t:expect-let-round name="VALUE_HIGH"
low="VALUE_FLOOR" />
</t:it>
<t:it desc="rounds down halves">
<t:expect-let-round name="VALUE_MID"
low="VALUE_FLOOR" />
</t:it>
<t:it desc="leaves integers unchanged">
<t:expect-let-round name="VALUE_FLOOR"
low="VALUE_FLOOR" />
<t:expect-let-round name="VALUE_CEIL"
low="VALUE_CEIL" />
</t:it>
</t:describe>
<t:describe name="nearest">
<t:it desc="rounds down low decimals">
<t:expect-let-round name="VALUE_LOW"
nearest="VALUE_FLOOR" />
</t:it>
<t:it desc="rounds up high decimals">
<t:expect-let-round name="VALUE_HIGH"
nearest="VALUE_CEIL" />
</t:it>
<t:it desc="rounds up halves">
<t:expect-let-round name="VALUE_MID"
nearest="VALUE_CEIL" />
</t:it>
<t:it desc="leaves integers unchanged">
<t:expect-let-round name="VALUE_FLOOR"
nearest="VALUE_FLOOR" />
<t:expect-let-round name="VALUE_CEIL"
nearest="VALUE_CEIL" />
</t:it>
</t:describe>
<t:describe name="with multiple names">
<t:it desc="produces each value">
<t:given>
<t:let-round name="VALUE_MID"
low="round_low"
high="round_high"
nearest="round_near">
<c:sum>
<c:value-of name="round_low" />
<c:value-of name="round_high" />
<c:value-of name="round_near" />
</c:sum>
</t:let-round>
</t:given>
<t:expect>
<!-- VALUE_FLOOR + VALUE_CEIL + VALUE_CEIL -->
<t:match-result eq="17" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with name index">
<t:it desc="produces respective rounded value">
<t:given>
<t:let-round name="VALUE_VEC"
index="VALUE_VEC_INDEX"
low="round_low"
high="round_high"
nearest="round_near">
<c:sum>
<c:value-of name="round_low" />
<c:value-of name="round_high" />
<c:value-of name="round_near" />
</c:sum>
</t:let-round>
</t:given>
<t:expect>
<!-- VALUE_FLOOR + VALUE_CEIL + VALUE_CEIL -->
<t:match-result eq="17" />
</t:expect>
</t:it>
</t:describe>
</t:describe>
<t:describe name="with a non-zero exp N">
<t:it desc="rounds before decimal point with positive exp">
<t:given>
<t:let-round name="VALUE_LARGE"
exp="#2"
low="round_low">
<c:value-of name="round_low" />
</t:let-round>
</t:given>
<!-- 1234.56 moved over two dec places = 12.3456; round that -->
<t:expect>
<t:match-result eq="1200" />
</t:expect>
</t:it>
<t:it desc="rounds after decimal point with negative exp">
<t:given>
<t:let-round name="VALUE_LARGE"
exp="#-1"
low="round_low">
<c:value-of name="round_low" />
</t:let-round>
</t:given>
<!-- 1234.56 moved over one dec place = 12345.6; round that -->
<t:expect>
<t:match-result eq="1234.50" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with a non-zero step N">
<t:it desc="rounds to the nearest arbitrary step">
<t:given>
<t:let-round name="VALUE_STEP"
step="#15"
low="round_low"
high="round_high">
<c:sum>
<c:value-of name="round_low" />
<c:value-of name="round_high" />
</c:sum>
</t:let-round>
</t:given>
<t:expect>
<!-- 30 + 45 -->
<t:match-result eq="75" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with both step and exp">
<t:it desc="steps after exponentiation">
<t:given>
<t:let-round name="VALUE_STEP"
step="#15"
exp="#-1"
low="round_low"
high="round_high">
<c:sum>
<c:value-of name="round_low" />
<c:value-of name="round_high" />
</c:sum>
</t:let-round>
</t:given>
<!-- 43.46 * 10^-1 = 434.6 / 15 = 28.973; ceil/floor that, then undo
step/exp to yield final result -->
<t:expect>
<!-- 42 + 43.5 -->
<t:match-result eq="85.5" />
</t:expect>
</t:it>
</t:describe>
</package>

View File

@ -0,0 +1,53 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2015, 2018 R-T Specialty, 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="Core test suite"
program="true">
<import package="../../base" />
<import package="../spec" />
<import package="numeric/convert" />
<import package="numeric/percent" />
<import package="numeric/round" />
<import package="vector/fold" />
<import package="vector/interpolate" />
<import package="vector/length" />
<import package="vector/stub" />
<import package="aggregate" />
<import package="insurance" />
<import package="symbol" />
<import package="tplgen" />
<!-- XXX broken!
<import package="ui" />
-->
<t:verify-specs result="expectOk" />
<yield>
<c:value-of name="expectOk" />
</yield>
</package>

View File

@ -0,0 +1,171 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="Vector Folding and Unfolding Specs">
<import package="../spec" />
<import package="../../vector/stub" />
<import package="../../base" />
<import package="../../symbol" />
<t:describe name="_if-symbol_">
<t:describe name="given no predicate">
<t:it desc="expands body if symbol is defined">
<t:given>
<c:sum>
<t:if-symbol name="TRUE">
<c:value-of name="TRUE" />
</t:if-symbol>
<c:value-of name="FALSE" />
</c:sum>
</t:given>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="does not expand body if symbol is undefined">
<t:given>
<c:sum>
<t:if-symbol name="UNDEFINED">
<c:value-of name="TRUE" />
</t:if-symbol>
<c:value-of name="FALSE" />
</c:sum>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="given type predicate">
<t:it desc="expands body if symbol is of given type">
<t:given>
<c:sum>
<t:if-symbol name="TRUE" type="const">
<c:value-of name="TRUE" />
</t:if-symbol>
<c:value-of name="FALSE" />
</c:sum>
</t:given>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="does not expand body if symbol is not of given type">
<t:given>
<c:sum>
<t:if-symbol name="TRUE" type="class">
<c:value-of name="TRUE" />
</t:if-symbol>
<c:value-of name="FALSE" />
</c:sum>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="given dim predicate">
<t:it desc="expands body if symbol dimenions matches">
<t:given>
<c:sum>
<t:if-symbol name="NVEC1" dim="1">
<c:value-of name="TRUE" />
</t:if-symbol>
<c:value-of name="FALSE" />
</c:sum>
</t:given>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="does not expand body if symbol does match dimensions">
<t:given>
<c:sum>
<t:if-symbol name="NVEC1" dim="2">
<c:value-of name="TRUE" />
</t:if-symbol>
<c:value-of name="FALSE" />
</c:sum>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
</t:describe>
<!-- this duplicates the above _if-symbol_ portion -->
<t:describe name="_if-defined_">
<t:describe name="given no predicate">
<t:it desc="expands body if symbol is defined">
<t:given>
<c:sum>
<t:if-defined name="TRUE">
<c:value-of name="TRUE" />
</t:if-defined>
<c:value-of name="FALSE" />
</c:sum>
</t:given>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="does not expand body if symbol is undefined">
<t:given>
<c:sum>
<t:if-defined name="UNDEFINED">
<c:value-of name="TRUE" />
</t:if-defined>
<c:value-of name="FALSE" />
</c:sum>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
</t:describe>
</package>

View File

@ -0,0 +1,67 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="Template Generation Package Specification">
<import package="../../test/spec" />
<import package="../../base" />
<import package="../../tplgen" />
<t:describe name="_for-each-n_">
<t:describe name="given a positive step">
<t:it desc="produces body N times exposing current N">
<t:given>
<c:sum>
<t:for-each-n start="2" end="6" step="2">
<c:const value="@current_n@" desc="Current N" />
</t:for-each-n>
</c:sum>
</t:given>
<t:expect>
<!-- 2 + 4 + 6 -->
<t:match-result eq="12" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="given a negative step">
<t:it desc="produces body N times exposing current N">
<t:given>
<c:sum>
<t:for-each-n start="4" end="1" step="-1">
<c:const value="@current_n@" desc="Current N" />
</t:for-each-n>
</c:sum>
</t:given>
<t:expect>
<!-- 4 + 3 + 2 + 1 = 4! (4 factorial) -->
<t:match-result eq="10" />
</t:expect>
</t:it>
</t:describe>
</t:describe>
</package>

View File

@ -0,0 +1,83 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2016 R-T Specialty, 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="Test UI integration">
<import package="../spec" />
<import package="../../base" />
<import package="../../ui" />
<t:describe name="_match-ui-applicable_">
The UI package generator will produce classifications of the form
{\tt --vis-FIELD}, where {\tt FIELD} has all underscores converted
into dashes. Others aren't supposed to know that, of course.
<t:it desc="Matches when visibility class is true">
<classify as="--vis-vis-true"
desc="Field class" />
<classify as="test-vis-true" desc="Test"
yields="testVisTrue">
<t:match-ui-applicable on="vis_true" />
</classify>
<t:given name="testVisTrue" />
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="Does not match when visibility class is false">
<classify as="--vis-vis-false" any="true"
desc="Field class" />
<classify as="test-vis-false" desc="Test"
yields="testVisFalse">
<t:match-ui-applicable on="vis_false" />
</classify>
<t:given name="testVisFalse" />
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:it desc="Can match against false visibility class">
<classify as="--vis-vis-true-false" any="true"
desc="Field class" />
<classify as="test-vis-true-false" desc="Test"
yields="testVisTrueFalse">
<t:match-ui-applicable on="vis_true_false"
value="FALSE"/>
</classify>
<t:given name="testVisTrueFalse" />
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
</t:describe>
</package>

View File

@ -0,0 +1,115 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="Vector Folding and Unfolding Specs">
<import package="../../spec" />
<import package="../../../base" />
<import package="../../../vector/fold" />
<import package="../../../vector/stub" />
<const name="UNFOLD_VEC_MID_EMPTY" type="integer"
desc="3-vector with middle unset">
<item value="1" desc="Set" />
<item value="0" desc="Unset" />
<item value="1" desc="Set" />
</const>
<t:vector-to-class name="UNFOLD_VEC_MID_EMPTY" as="unfold-mid-empty" />
<const name="UNFOLD_VEC_3GROUP" type="integer"
desc="3-grouping for test case"
values="1,0,2,2,1,2" />
<!-- given the sequence [0 1 2 3 4 5] -->
<const name="UNFOLD_VEC_3GROUP_EXPECTED" type="integer"
desc="Expected result"
values="1; 0,4; 2,3,5" />
<const name="UNFOLD_3VEC_ONE_EACH" type="integer"
desc="3-vector with one in each group">
</const>
<t:describe name="_unfold-vector-grouped_">
<t:it desc="yields a vector whose length is that of the class">
<t:unfold-vector-grouped class="nclass3"
generates="unfoldVecGroupedLenClass"
src="NVEC1"
grouping="NVEC1" />
<t:given>
<c:length-of>
<c:value-of name="unfoldVecGroupedLenClass" />
</c:length-of>
</t:given>
<t:expect>
<t:match-result eq="3" />
</t:expect>
</t:it>
<t:it desc="uses grouping vector for grouping">
<t:unfold-vector-grouped class="nclass3"
generates="unfoldVecGrouped"
src="NVEC6_SEQ"
grouping="UNFOLD_VEC_3GROUP" />
<!-- TODO: matrix classification matches -->
</t:it>
<t:it desc="does not include non-matching groups">
<t:unfold-vector-grouped class="unfold-mid-empty"
generates="unfoldVecGroupedNonClassMatch"
src="NVEC3_SEQ"
grouping="NVEC3_SEQ" />
<t:given>
<c:sum>
<c:length-of>
<c:value-of name="unfoldVecGroupedNonClassMatch" index="#0" />
</c:length-of>
<c:length-of>
<c:value-of name="unfoldVecGroupedNonClassMatch" index="#1" />
</c:length-of>
<c:length-of>
<c:value-of name="unfoldVecGroupedNonClassMatch" index="#2" />
</c:length-of>
<!-- sum a second time to make sure it's 0 -->
<c:length-of>
<c:value-of name="unfoldVecGroupedNonClassMatch" index="#1" />
</c:length-of>
</c:sum>
</t:given>
<t:expect>
<t:match-result eq="2" />
</t:expect>
</t:it>
</t:describe>
</package>

View File

@ -0,0 +1,336 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2015 R-T Specialty, 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="Interpolation specs">
<import package="../../../base" />
<import package="../../../numeric/common" />
<import package="../../../numeric/minmax" />
<import package="../../../numeric/round" />
<import package="../../../vector/arithmetic" />
<import package="../../spec" />
<import package="../../../vector/table" />
<import package="../../../vector/interpolate" />
<const name="INTERP_TABLE_STEP"
value="100" type="integer"
desc="Test table step" />
<!-- table containing test data -->
<t:create-table name="interp-query-field-test"
desc="Test data for query interpolation">
<t:table-column name="key"
index="0"
desc="Lookup value" />
<t:table-column name="pred"
index="1"
desc="Random value for predicate testing" />
<t:table-column name="value"
index="2"
desc="Value to interpolate" />
<t:table-rows>
<t:table-row>
<t:table-value const="100" />
<t:table-value const="10" />
<t:table-value const="1" />
</t:table-row>
<t:table-row>
<t:table-value const="200" />
<t:table-value const="20" />
<t:table-value const="2" />
</t:table-row>
<!-- two values for predicate testing -->
<t:table-row>
<t:table-value const="300" />
<t:table-value const="30" />
<t:table-value const="3" />
</t:table-row>
<t:table-row>
<t:table-value const="300" />
<t:table-value const="31" />
<t:table-value const="3.5" />
</t:table-row>
<t:table-row>
<t:table-value const="400" />
<t:table-value const="40" />
<t:table-value const="4" />
</t:table-row>
<t:table-row>
<t:table-value const="400" />
<t:table-value const="31" />
<t:table-value const="4" />
</t:table-row>
</t:table-rows>
</t:create-table>
<const name="INTERP_TABLE_SM_STEP"
value="15" type="integer"
desc="Small-step test table step" />
<!-- test another step (we didn't hardcode 100, did we)? -->
<t:create-table name="interp-query-field-test-sm"
desc="Test data for query interpolation; sm step">
<t:table-column name="key"
index="0"
desc="Lookup value" />
<t:table-column name="value"
index="1"
desc="Value to interpolate" />
<t:table-rows>
<t:table-row>
<t:table-value const="15" />
<t:table-value const="0" />
</t:table-row>
<t:table-row>
<t:table-value const="30" />
<t:table-value const="10" />
</t:table-row>
</t:table-rows>
</t:create-table>
<!-- by avoiding hardcoding these, we make these tests less fragile
(if the test data were to change) -->
<section title="Common inferable table values">
<rate yields="interpTableMaxKeyValue">
<t:maxreduce isvector="true">
<t:query-field table="interp-query-field-test"
field="key" />
</t:maxreduce>
</rate>
<rate yields="interpTableMaxFieldValue">
<t:query-first-field table="interp-query-field-test"
field="value">
<t:when field="key">
<c:value-of name="interpTableMaxKeyValue" />
</t:when>
</t:query-first-field>
</rate>
</section>
<t:describe name="_interpolate-query-field_">
<t:describe name="exact value query">
<!-- a simple expression with no body performs basic interpolation
based on a query result...just like that! -->
<t:it desc="succeeds without predicates">
<t:given>
<t:interpolate-query-field table="interp-query-field-test"
field="value"
key="key"
step="INTERP_TABLE_STEP"
actual="#100" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#1" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<!-- sanity check -->
<t:it desc="succeeds with predicates">
<t:given>
<t:interpolate-query-field table="interp-query-field-test"
field="value"
key="key"
step="INTERP_TABLE_STEP"
actual="#300">
<t:when field="pred">
<c:const value="31" type="float"
desc="Test predicate value" />
</t:when>
</t:interpolate-query-field>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#3.5" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<t:describe name="inexact value query">
<t:it desc="performs interpolation on query field">
<!-- take that you f@%*#^! PITA algorithms -->
<t:given>
<t:interpolate-query-field table="interp-query-field-test"
field="value"
key="key"
step="INTERP_TABLE_STEP"
actual="#150" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<!-- this value does not exist in the table; it is
interpolated, just like real magic -->
<c:value-of name="#1.5" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<!-- take note that the predicate applies to *both* rows
returned by the query -->
<t:it desc="performs interpolation considering predicates">
<t:given>
<t:interpolate-query-field table="interp-query-field-test"
field="value"
key="key"
step="INTERP_TABLE_STEP"
actual="#350">
<t:when field="pred">
<c:const value="31" type="float"
desc="Test predicate value" />
</t:when>
</t:interpolate-query-field>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<!-- note that this interpolates between 3.5 and 4 -->
<c:value-of name="#3.75" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<!-- ensure that the step is indeed configurable -->
<t:it desc="permits arbitrary step">
<t:given>
<t:interpolate-query-field table="interp-query-field-test-sm"
field="value"
key="key"
step="INTERP_TABLE_SM_STEP"
actual="#22.5" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#5" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<t:describe name="with actual exceeding maximum">
<t:describe name="with no step factor">
<t:it desc="yields value of maximum key">
<t:given>
<t:interpolate-query-field table="interp-query-field-test"
field="value"
key="key"
step="INTERP_TABLE_STEP"
table_max="interpTableMaxKeyValue"
actual="#10000" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="interpTableMaxFieldValue" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<!-- we can infer values past the maximum -->
<!-- TODO: these are fragile, because we'd otherwise be
duplicating a lot of logic; let's factor some common logic
out of _interpolate-query-field_ -->
<t:describe name="with step factor">
<t:it desc="applies arbitrary factor linearly to difference">
<t:given>
<!-- note that our step factor differs from the previous
field value step in the table -->
<t:interpolate-query-field table="interp-query-field-test"
field="value"
key="key"
step="INTERP_TABLE_STEP"
step_factor="#2"
table_max="interpTableMaxKeyValue"
actual="#600" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#8" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
<!-- half-step relative to above expectation -->
<t:it desc="interpolates linear factor">
<t:given>
<t:interpolate-query-field table="interp-query-field-test"
field="value"
key="key"
step="INTERP_TABLE_STEP"
step_factor="#2"
table_max="interpTableMaxKeyValue"
actual="#650" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#9" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
</t:describe>
</t:describe>
</package>

View File

@ -0,0 +1,143 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2017 R-T Specialty, 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="Interpolation specs">
<import package="../../spec" />
<import package="../../../base" />
<import package="../../../vector/length" />
<t:describe name="_first-nonempty_">
<t:describe name="given no vectors">
<t:it desc="yields an empty vector">
<t:given>
<c:length-of>
<t:first-nonempty />
</c:length-of>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#0" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<t:describe name="given only one empty vector">
<t:it desc="yields an empty vector">
<t:given>
<c:length-of>
<t:first-nonempty>
<c:set />
</t:first-nonempty>
</c:length-of>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#0" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<t:describe name="given only empty vectors">
<t:it desc="yields an empty vector">
<t:given>
<c:length-of>
<t:first-nonempty>
<c:set />
<c:set />
</t:first-nonempty>
</c:length-of>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#0" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<t:describe name="given first empty vector with non-empty">
<t:it desc="yields the second vector">
<t:given>
<c:car>
<t:first-nonempty>
<c:set />
<c:set>
<c:const value="50" desc="Non-empty vector" />
</c:set>
</t:first-nonempty>
</c:car>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#50" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
<t:describe name="given two non-empty vectors">
<t:it desc="yields the first vector">
<t:given>
<c:car>
<t:first-nonempty>
<c:set>
<c:const value="60" desc="Non-empty vector" />
</c:set>
<c:set>
<c:const value="70" desc="Non-empty vector" />
</c:set>
</t:first-nonempty>
</c:car>
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#60" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
</t:describe>
</package>

View File

@ -0,0 +1,49 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="Vector Stub Package Specification">
<import package="../../../test/spec" />
<import package="../../../base" />
<import package="../../../vector/stub" />
<t:n-vector n="5" name="TEST_N_VEC" value="1" />
<t:describe name="_n-vector_">
<t:it desc="generates vector of length N">
<t:given>
<c:sum of="TEST_N_VEC" />
</t:given>
<t:expect>
<t:match-result>
<c:eq>
<c:value-of name="#5" />
</c:eq>
</t:match-result>
</t:expect>
</t:it>
</t:describe>
</package>

355
core/test/spec.xml 100644
View File

@ -0,0 +1,355 @@
<?xml version="1.0"?>
<!--
BDD specification framework
Copyright (C) 2015, 2018 R-T Specialty, 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/>.
This framework uses specifications, not stories[0]. Its syntax is heavily
motivated by popular BDD frameworks such as RSpec (Ruby), Jasmine
(JavaScript), and Mocha (JavaScript).
As an example, consider a specification for a simple stack implementation:
describe stack
describe push
it increases the stack size by 1
describe to an empty stack
it produces a stack of size 1
describe pop
it decreases the stack size by 1
it returns the most recently pushed item
describe multiple times
it returns elements in reverse order of push
describe from an empty stack
it produces an error
These templates expand all definitions flatly into their parent node;
descriptions should therefore be in the package root or within nodes that
support calculation and classification definitions.
[0]: http://en.wikipedia.org/wiki/Behavior-driven_development
#Story_versus_specification
-->
<package xmlns="http://www.lovullo.com/rater"
xmlns:t="http://www.lovullo.com/rater/apply-template"
xmlns:c="http://www.lovullo.com/calc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.lovullo.com/rater ../../rater.xsd"
core="true"
name="core/test/spec"
desc="Behavior-driven development specification system">
<import package="../base" />
<import package="../vector/cmatch"
export="true" />
<template name="_verify-specs_"
desc="Declare a template suite">
<param name="@result@" desc="Name of boolean result" />
<!--
Determines whether all features are conformant to their
specifications; this classification is a simple way to determine
whether a test suite has passed.
-->
<classify as="expect-ok"
yields="@result@"
desc="All features conform to specifications"
keep="true">
<inline-template>
<for-each>
<sym-set name-prefix="expect-conform-"
type="class" />
</for-each>
<t:match-class name="{@sym_name@}" />
</inline-template>
</classify>
</template>
<!--
Describe a feature specification
Each specification has a name that describes the feature as concisely as
possible. For example, if testing a stack, then `@name@` should simply
be "stack".
Specifications may be arbitrarily nested to group together
sub-features. If testing a specific feature of a parent feature, defer
as much of the description to the "it" clauses as possible.
A high-level classification will be generated that will assert on the
results of all expectations contained therein.
Permitted children:
- _describe_* - describe sub-features
- _it_+ - describe expectations
-->
<template name="_describe_"
desc="Describe a specification">
<param name="@name@" desc="Subject (SUT)" />
<param name="@values@" desc="Specification descriptions" />
<param name="@__full_name@"
desc="Full subject name, inherited from parents">
<param-inherit meta="spec-name" />
<param-value name="@name@" />
<text> </text>
</param>
<param name="@__prefix@"
desc="Generated expectation prefix">
<!-- descriptions may be nested; this may or may not exist -->
<param-inherit meta="spec-prefix" />
<param-value name="@name@" dash="true" identifier="true" />
<text>-</text>
</param>
<param name="@__uniq@"
desc="Unique id">
<text unique="true" />
</param>
<expand-sequence>
<expand-group>
<param-copy name="@values@">
<param-meta name="spec-name"
value="@__full_name@" />
<param-meta name="spec-prefix"
value="@__prefix@" />
</param-copy>
</expand-group>
<!-- joins all generated classifications to provide a higher-level
failure if any expectations fail -->
<classify as="expect-conform-{@__prefix@}{@__uniq@}"
desc="{@__full_name@} meets expectations"
keep="true">
<inline-template>
<for-each>
<sym-set name-prefix="expect-that-{@__prefix@}"
type="class" />
</for-each>
<t:match-class name="{@sym_name@}" />
</inline-template>
</classify>
</expand-sequence>
</template>
<!--
Describe a logical group of expectations
While `_describe_` delimits features (or sub-features), `_it_` delimits
logical groups of expectations of those features. Each expectation
within an `_it_` group can assert on a common `_given_` value.
The description should complete the sentence that started at the
uppermost `_describe_` ancestor; see the examples at the head of this
package.
Permitted children:
- * - arbitrary definitions for feature test
- _given_? - describe common data for expectations
- _expect_+ - describe feature expectation
-->
<template name="_it_"
desc="Describe logical group of expectations">
<param name="@desc@" desc="Description of expectation" />
<param name="@values@" desc="Expectations" />
<param name="@__id@" desc="Unique identifier">
<param-inherit meta="spec-prefix" />
<param-value name="@desc@" dash="true" identifier="true" />
</param>
<param-copy name="@values@">
<param-meta name="spec-it"
value="@desc@" />
<param-meta name="spec-it-prefix"
value="@__id@" />
</param-copy>
</template>
<!--
Describe a value for expectation groups
The defined value is available to adjacent expectations through use of
`_match-result`.
A `_given_` definition is not required; it exists as a convenient and
concise way to represent test data.
Permitted children:
- Any valid calculation
-->
<template name="_given_"
desc="Describe a value for expectation groups">
<param name="@values@" desc="Calculation" />
<param name="@name@" desc="Value to reference (optional)" />
<param name="@__id@"
desc="Unique identifier to avoid symbol conflicts">
<text unique="true">given</text>
</param>
<!-- name given; no need to generate -->
<if name="@name@">
<param-meta name="spec-given-id"
value="@name@" />
</if>
<!-- no name given; generate a unique one -->
<unless name="@name@">
<param-meta name="spec-given-id"
value="@__id@" />
<rate yields="@__id@">
<param-copy name="@values@" />
</rate>
</unless>
</template>
<!--
Describe a feature expectation
An expectation tests the adherence of a feature to its specification. It
generates a classification and therefore shares identical child nodes.
Expectations may assert upon any value within scope. The
`_given_` template exists to simplify test calculation definitions;
asserting on the result of the adjacent `_given_` can be done using
`_match-result_`.
Permitted children:
- Any valid classification predicates
-->
<template name="_expect_"
desc="Describe a feature expectation">
<param name="@values@"
desc="Predicates" />
<!-- generated by the _describe_ template -->
<param name="@__spec_name@"
desc="Inherit specification name">
<param-inherit meta="spec-name" />
</param>
<!-- generated by the _it_ template -->
<param name="@__spec_it@"
desc="Inherit specification clause">
<param-inherit meta="spec-it" />
</param>
<!-- generate a unique id to avoid class conflicts (be sure to use the
ineherited prefix so that they can all be combined into a larger
predicate to assert on failing tests) -->
<param name="@__id@"
desc="Unique identifier">
<text>expect-that-</text>
<param-inherit meta="spec-it-prefix" />
<text unique="true">-</text>
</param>
<classify as="@__id@"
desc="{@__spec_name@}{@__spec_it@}">
<param-copy name="@values@">
<param-meta name="spec-expect-id"
value="@__id@" />
</param-copy>
</classify>
</template>
<!--
Match against result of an adjacent `_given_` expression
This has equivalent syntax to the primitive `match`, except that `@on`
is determined for you.
Permitted children:
- Anything permitted by the `match` primitive
-->
<template name="_match-result_"
desc="Match against result of an adjacent _given_ expression">
<param name="@values@"
desc="Calculation as predicate">
<text></text>
</param>
<param name="@index@" desc="Constant index">
<text></text>
</param>
<param name="@__given_id@"
desc="Unique identifier of _given_ expression">
<param-inherit meta="spec-given-id" />
</param>
<!-- choose one -->
<param name="@value@" desc="Match value" />
<param name="@eq@" desc="Match equality" />
<param name="@anyOf@" desc="Match against domain of type" />
<if name="@value@">
<match on="@__given_id@" index="@index@" value="@value@" />
</if>
<unless name="@value@">
<if name="@eq@">
<match on="@__given_id@" index="@index@">
<c:eq>
<c:value-of name="#{@eq@}" />
</c:eq>
</match>
</if>
<unless name="@eq@">
<if name="@anyOf@">
<match on="@__given_id@" index="@index@" anyOf="@anyOf@" />
</if>
<unless name="@anyOf@">
<match on="@__given_id@" index="@index@">
<param-copy name="@values@" />
</match>
</unless>
</unless>
</unless>
</template>
</package>

77
core/tplgen.xml 100644
View File

@ -0,0 +1,77 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="Template Generation">
This package provides various templates for generating templates and
iterating using templates.
In essense,
this package is intended to abstract away certain implementation details
and complexities that make groking code more difficult;
they should be used when possible to improve code maintenance.
<section title="Iteration">
\ref{_for-each-n_} recurisvely produces the body~\tt{@values@} given a
numeric range and step.
This can also be used to generate sequences at compile-time rather than
using functions,
provided that the sequence data are static.
<template name="_for-each-n_"
desc="Recursively apply body with counter and step">
<param name="@values@" desc="Body" />
<param name="@start@" desc="Counter start" />
<param name="@end@" desc="Counter end" />
<param name="@step@" desc="Counter step" />
<param name="@next_n@" desc="Next iteration value">
<param-add name="@start@" value="@step@" />
</param>
<param name="@n_dec@" desc="Current n - 1">
<param-add name="@start@" value="-1" />
</param>
<!-- inefficient trick to expose @current_n@ to the body -->
<inline-template>
<for-each>
<set current_n="@start@" current_n_dec="@n_dec@" />
</for-each>
<param-copy name="@values@" />
</inline-template>
<!-- repeat body for each step, as sibling -->
<unless name="@start@" eq="@end@">
<t:for-each-n start="@next_n@" end="@end@" step="@step@">
<param-copy name="@values@" />
</t:for-each-n>
</unless>
</template>
</section>
</package>

93
core/ui.xml 100644
View File

@ -0,0 +1,93 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2016 R-T Specialty, 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"
title="UI Integration">
<import package="base" />
<!-- contains template dependencies -->
<import package="/rater/core/vector/cmatch" export="true" />
This package provides elementary integration with the UI through
mystical knowledge of the naming conventions that the compiler uses
when generating the UI package.
You should \emph{never} reference generated values yourself without
an abstraction.
<section title="Applicability">
An object in the UI is considered to be \dfn{applicable} if the
predicate defined by its {\tt @when} attribute matches. These
predicates are generated, and should not be referenced directly;
instead, \ref{_match-ui-applicable_} should be used with the
question id.
The term ``applicable'' is used instead of ``visible'' because
object can be hidden by other means; a predicate might be true
while a field is actually hidden. Further, a non-matching
predicate inhibits other behavior, like running of assertions.
Note that this template will only be useful with an object
(question, display, static, etc) that has a predicate defined;
otherwise, compilation will fail.
<template name="_match-ui-applicable_"
desc="Match whether a UI question is visible">
<param name="@on@" desc="Question id" />
<param name="@value@" desc="Value to match">
<text>TRUE</text>
</param>
<param name="@__vis_class@" desc="Generated visibility class name">
<text>--vis-</text>
<param-value name="@on@" dash="true" />
</param>
<t:match-class name="@__vis_class@" value="@value@" />
</template>
\ref{_match-ui-set_} determines whether a UI field has a truthful value,
which is non-empty and non-zero.
<template name="_match-ui-set_"
desc="Match whether a UI question is set">
<param name="@on@" desc="Question id" />
<param name="@value@" desc="Whether set">
<text>TRUE</text>
</param>
<param name="@__set_class@" desc="Generated set class name">
<text>--set-</text>
<param-value name="@on@" dash="true" />
</param>
<t:match-class name="@__set_class@" value="@value@" />
</template>
</section>
</package>

37
core/vector.xml 100644
View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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"
core="true"
desc="Vector operations">
<!-- we are a meta-package -->
<import package="vector/arithmetic" export="true" />
<import package="vector/cmatch" export="true" />
<import package="vector/convert" export="true" />
<import package="vector/count" export="true" />
<import package="vector/list" export="true" />
<import package="vector/matrix" export="true" />
<import package="vector/table" export="true" />
<import package="vector/common" export="true" />
</package>

View File

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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="Vector arithmetic">
<import package="../base" />
<import package="../numeric/common" />
<import package="../numeric/minmax" />
<import package="list" />
<section title="Vectors">
<!-- for the time being, the vectors must be of the same length, or the first
vector must be the longer (otherwise the values will not properly add)
-->
<template name="_vadd_" desc="Produce a vector resulting from the addition of two vectors">
<param name="@a@" desc="First vector to add" />
<param name="@b@" desc="Second vector to add" />
<param name="@into@" desc="Variable to yield vector into" />
<param name="@yields@" desc="Value to yield (useless)">
<text>__</text>
<param-value name="@into@" />
</param>
<param name="@gendesc@" desc="Generator description (for @into@)">
<text>Sum of vectors </text>
<param-value name="@a@" />
<text> and </text>
<param-value name="@b@" />
</param>
<param name="@sym@" desc="Generator symbol">
<!-- empty by default -->
<text></text>
</param>
<rate accumulate="none" yields="@yields@">
<c:sum of="@a@" index="k" generates="@into@" desc="@gendesc@" sym="@sym@">
<c:value-of name="@a@" index="k" />
<c:value-of name="@b@" index="k" />
</c:sum>
</rate>
</template>
<template name="_vsum_" desc="Yield sum of all elements of a vector as a scalar">
<param name="@of@" desc="Vector to sum" />
<param name="@yields@" desc="Name of scalar to yield" />
<rate yields="@yields@">
<c:sum of="@of@" />
</rate>
</template>
\ref{_vproduct_} produces the product of two vectors:
$V_k = A_k B_k$.
<template name="_vproduct_"
desc="Vector product">
<param name="@vector_a@" desc="First vector" />
<param name="@vector_b@" desc="Second vector" />
<c:apply name="_vproduct"
vector_a="@vector_a@"
vector_b="@vector_b@" />
</template>
\ref{_vproduct} is its helper function for recursion.
<function name="_vproduct"
desc="Vector product">
<param name="vector_a" type="float" set="vector"
desc="First vector" />
<param name="vector_b" type="float" set="vector"
desc="Second vector" />
<param name="k" type="integer"
desc="Current index" />
<t:cons-until-empty set="vector_a" index="k" car="value_a">
<c:product>
<c:value-of name="value_a" />
<!-- TODO: TAME bug where index variables always compile to args -->
<c:value-of name="vector_b">
<c:index>
<c:value-of name="k" />
</c:index>
</c:value-of>
</c:product>
</t:cons-until-empty>
</function>
</section>
<section title="Matrices">
\ref{_mproduct_} produces the product of two matrices:
$M_{ij} = A_{ij} B_{ij}$.
<template name="_mproduct_"
desc="Matrix product">
<param name="@matrix_a@" desc="First matrix" />
<param name="@matrix_b@" desc="Second matrix" />
<c:apply name="_mproduct"
matrix_a="@matrix_a@"
matrix_b="@matrix_b@"
k="ZERO" />
</template>
\ref{_mproduct} is its helper function for recursion.
\ref{_vproduct_} is used to reduce the problem to the product of
and array of~vectors.
<function name="_mproduct"
desc="Matrix product">
<param name="matrix_a" type="float" set="matrix"
desc="First matrix" />
<param name="matrix_b" type="float" set="matrix"
desc="Second matrix" />
<param name="k" type="integer"
desc="Current index" />
<t:cons-until-empty set="matrix_a" index="k" car="vector_a">
<c:let>
<c:values>
<c:value name="vector_b" type="float" set="vector"
desc="Second vector">
<c:value-of name="matrix_b">
<c:index>
<c:value-of name="k" />
</c:index>
</c:value-of>
</c:value>
</c:values>
<t:vproduct vector_a="vector_a"
vector_b="vector_b" />
</c:let>
</t:cons-until-empty>
</function>
</section>
</package>

View File

@ -0,0 +1,254 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015, 2018 R-T Specialty, 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="Vector operations on classification matches">
<import package="../base" />
<import package="arithmetic" export="true" />
<import package="minmax" export="true" />
<!--
This wonderful little gem takes a classification match and stores it in a
vector, allowing for hassle-free use in calculations without awkward
hasany() calls on classification matrices (where a classification yields a
matrix, that is). This is useless for classifications that yield vectors,
since the result will be the same.
This template is also useful for combining various classification vectors
and matrices, whoose reduction would otherwise be a bit complicated. Of
course, the alternative there is also to create a classification that
relies on other classifications; use your best judgement.
This exploits the fact that _CMATCH_'s are always vectors, even if we are
matching on a matrix. As such, the system has already gone through the
trouble of reducing the matrix into a vector for us, so all we need to do
is store each value into a vector, which can be easily accomplished using a
generator.
This is an excellent example of building a feature atop of the DSL without
having to add a new language feature.
-->
<template name="_cmatch-to-vector_" desc="Vectorizes a classification match">
<param name="@class@" desc="Classification match string" />
<param name="@generates@" desc="Variable to yield generates (will yield a vector)" />
<param name="@keep@" desc="Rate block @keep">
<text></text>
</param>
<param name="@yields@" desc="Dummy variable to yield generates (useless, but required)">
<text>__</text>
<param-value name="@generates@" />
</param>
<param name="@gendesc@" desc="Generator description">
<text>Vector containing boolean </text>
<param-value name="@class@" />
<text> classification matches</text>
</param>
<param name="@sym@" desc="Generator symbol (corresponds to @generates@)">
<!-- defaults to nothing -->
<text></text>
</param>
<!-- this conversion is as simple as using a generator to yield the value
of _CMATCH_ for each index -->
<rate class="@class@" accumulate="none" yields="@yields@" always="true" keep="@keep@">
<c:sum of="_CMATCH_" index="k" generates="@generates@" desc="@gendesc@" sym="@sym@">
<c:value-of name="_CMATCH_" index="k" />
</c:sum>
</rate>
</template>
<template name="_cmatch-to-scalar_" desc="Reduces a classification match into a scalar">
<param name="@class@" desc="Classification match string" />
<param name="@yields@" desc="Variable to yield into" />
<param name="@sym@" desc="Yield symbol (defaults to nothing)">
<!-- defaults to nothing -->
<text></text>
</param>
<param name="@keep@" desc="Rate block @keep">
<text></text>
</param>
<rate class="@class@" accumulate="none" yields="@yields@" sym="@sym@" keep="@keep@">
<!-- if any single one matches, then we want to yield a 1 -->
<c:apply name="maxreduce" maxreduce_set="_CMATCH_" />
</rate>
</template>
In cases where a classification needs to be based on a result that
has been reduced to a scalar, \ref{_cmatch_to-scalar_} does not
solve the whole problem: for this, \ref{_classify-scalar_} may be
used. In addition to performing the action of the former (if {\tt
@yields} is provided), the resulting classification itself will
match on the scalar result. While this is not strictly
necessary---the predicate itself is already scalar---this is
important for systems or templates that derive the classification
result from the name of the classification.
<template name="_classify-scalar_"
desc="Classification with a forced-scalar result">
<param name="@values@" desc="Predicates" />
<param name="@as@" desc="Classification name" />
<param name="@desc@" desc="Classification description" />
<param name="@yields@" desc="Scalar result name">
<text>__</text>
<param-value snake="true" name="@as@" />
</param>
<param name="@keep@" desc="Whether to force compilation">
<text></text>
</param>
<param name="@sym@" desc="Optional yield symbol">
<text></text>
</param>
<classify as="--{@as@}-pre"
yields="__{@yields@}Pre"
desc="{@desc@}, pre-scalar">
<param-copy name="@values@" />
</classify>
<t:cmatch-to-scalar class="--{@as@}-pre"
yields="__{@yields@}Scalar"
sym="@sym@"
keep="@keep@" />
<classify as="@as@" yields="@yields@"
desc="@desc@"
keep="@keep@"
sym="@sym@">
<match on="__{@yields@}Scalar" />
</classify>
</template>
<!--
Counts one for each classification vector match
-->
<template name="_cmatch-count_" desc="Counts the number of classification matches of a vector">
<param name="@class@" desc="Classification match" />
<param name="@yields@" desc="Value to yield" />
<!-- nothing by default -->
<param name="@no@" desc="Non-match">
<text></text>
</param>
<rate class="@class@" no="@no@" yields="@yields@">
<c:sum of="_CMATCH_" />
</rate>
</template>
<!-- because verbose repition is an evil -->
<inline-template>
<for-each>
<set cmp="eq" />
<set cmp="ne" />
<set cmp="gt" />
<set cmp="gte" />
<set cmp="lt" />
<set cmp="lte" />
</for-each>
<template name="_match-{@cmp@}_" desc="Match value {@cmp@}">
<param name="@on@" desc="Value to assert" />
<!-- pick one -->
<param name="@const@" desc="Match against constant value" />
<param name="@value@" desc="Match against variable" />
<if name="@const@">
<warning>
@const@ is deprecated; use @value@ with a #-prefix instead.
</warning>
</if>
<match on="@on@">
<dyn-node name="c:{@cmp@}">
<if name="@const@">
<c:const value="@const@" type="float" desc="Comparison" />
</if>
<unless name="@const@">
<c:value-of name="@value@" />
</unless>
</dyn-node>
</match>
</template>
</inline-template>
<template name="_match-class_"
desc="Match on a class name (rather than @yields)">
<param name="@name@" desc="Classification name" />
<param name="@__yields@" desc="Classification yield to match on">
<param-class-to-yields name="@name@" />
</param>
<param name="@value@" desc="Value to match on">
<text></text>
</param>
<match on="@__yields@" value="@value@" />
</template>
\ref{_vector-to-class_} converts a vector into a classification.
<template name="_vector-to-class_"
desc="Convert a vector to a classification">
<param name="@name@" desc="Source vector" />
<param name="@as@" desc="Classification name" />
<param name="@yields@" desc="Classification yield">
<text></text>
</param>
<param name="@desc@" desc="Classification description">
<text>Classification of </text>
<param-value name="@name@" />
</param>
<classify as="@as@" desc="@desc@" yields="@yields@">
<match on="@name@" />
</classify>
</template>
</package>

View File

@ -0,0 +1,205 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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="General vector operations">
<import package="../base" />
<import package="../numeric/common" />
<!-- useful because you can pass a conditional expression as an argument and
select the index of the result -->
<function name="vival" desc="Select index value from some vector">
<param name="vector" type="float" set="vector" desc="Vector" />
<param name="index" type="integer" desc="Index" />
<c:value-of name="vector">
<c:index>
<c:value-of name="index" />
</c:index>
</c:value-of>
</function>
<function name="mival" desc="Select index value from some matrix">
<param name="matrix" type="float" set="matrix" desc="Matrix" />
<param name="row" type="integer" desc="Row index" />
<param name="col" type="integer" desc="Column index" />
<c:value-of name="matrix">
<c:index>
<c:value-of name="row" />
</c:index>
<c:index>
<c:value-of name="col" />
</c:index>
</c:value-of>
</function>
<template name="_map-value_" desc="Map from a value to another using a vector map">
<param name="@from@" desc="Name of value to map" />
<param name="@index@" desc="Index" />
<param name="@using@" desc="Vector to use for mapping" />
<c:apply name="vival">
<c:arg name="vector">
<c:value-of name="@using@" />
</c:arg>
<c:arg name="index">
<c:value-of name="@from@" index="@index@" />
</c:arg>
</c:apply>
</template>
<!--
Look up a matrix value using maps for both row and column indexes
This allows for the same values to be used with different matrices. The maps
will be used to map a value to an index within the matrix for either a row
or column.
-->
<function name="mlookup" desc="Matrix value lookup based on two index maps">
<param name="matrix" type="float" set="matrix" desc="Rate matrix indexed by CT and PC" />
<param name="rmap" type="integer" set="vector" desc="Row index map" />
<param name="cmap" type="integer" set="vector" desc="Column index map" />
<param name="rval" type="integer" desc="Row value (to be fed to map for index lookup)" />
<param name="cval" type="integer" desc="Column value (to be fed to map for index lookup)" />
<c:value-of name="matrix">
<!-- row -->
<c:index>
<c:value-of name="rmap">
<c:index>
<c:value-of name="rval" />
</c:index>
</c:value-of>
</c:index>
<!-- column -->
<c:index>
<c:value-of name="cmap">
<c:index>
<c:value-of name="cval" />
</c:index>
</c:value-of>
</c:index>
</c:value-of>
</function>
<function name="first_index" desc="Determine the matching index of a vector; -1 if no match">
<param name="vector" type="float" set="vector" desc="Source vector to search" />
<param name="value" type="integer" desc="Value to match" />
<param name="offset" type="integer" desc="Vector offset" />
<c:let>
<c:values>
<!-- TODO: do not calculate every time -->
<c:value name="vlen" type="integer" desc="Vector length">
<c:length-of>
<c:value-of name="vector" />
</c:length-of>
</c:value>
</c:values>
<c:cases>
<c:case>
<c:when name="offset">
<c:gte>
<c:value-of name="vlen" />
</c:gte>
</c:when>
<c:const value="-1" type="integer" desc="Not found" />
</c:case>
<c:case>
<c:when name="vector" index="offset">
<c:eq>
<c:value-of name="value" />
</c:eq>
</c:when>
<!-- found the index -->
<c:value-of name="offset" />
</c:case>
<c:otherwise>
<c:recurse vector="vector" value="value">
<c:arg name="offset">
<t:inc>
<c:value-of name="offset" />
</t:inc>
</c:arg>
</c:recurse>
</c:otherwise>
</c:cases>
</c:let>
</function>
The template \ref{_repeat-value_} will generate a vector from the
given value for each class match.
<template name="_repeat-value_"
desc="Repeat value for each class match">
<param name="@values@" desc="Calculation producing value (use
one of this or @value@)" />
<param name="@class@" desc="Classification to consider" />
<param name="@value@" desc="Value to repeat (use one of this
or @values@)" />
<param name="@generates@" desc="Result value (vector)" />
<!-- if providing @values@ -->
<param name="@index@" desc="Generator index">
<text>__k</text>
</param>
<rate-each class="@class@"
generates="@generates@" index="@index@">
<if name="@values@">
<param-copy name="@values@" />
</if>
<unless name="@values@">
<c:value-of name="@value@" index="@index@" />
</unless>
</rate-each>
</template>
<!-- generates a variable that can be recognized as an empty set (useful for
defaults to params that require sets) -->
<rate-each class="always" yields="__empty" generates="__emptySet" index="k">
<c:const value="0" type="integer" desc="Nothing" />
</rate-each>
</package>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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"
core="true"
desc="Convert vectors into other types">
<import package="../base" />
</package>

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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"
core="true"
desc="Vector element counting">
<import package="../base" />
<!-- TODO: deprecate in favor of the new c:length-of -->
<function name="length" desc="Counts the number of items in a vector (regardless of value)">
<param name="count_set" type="integer" set="vector" desc="Vector to count" />
<c:sum of="count_set" index="k">
<c:const value="1" type="integer" desc="Add 1 for each value in the set" />
</c:sum>
</function>
<!-- similar to a logical "exists" -->
<function name="hasany" desc="Returns 1 or 0 based on whether any values in a boolean vector are set" sym="\exists">
<param name="any_set" type="boolean" set="vector" desc="Boolean set to check" />
<c:ceil>
<c:quotient label="Create a fraction > 0 if any value in the given set is 1">
<c:sum of="any_set" label="Will be > 0 if any 1s exist in the set" />
<c:sum>
<c:apply name="length">
<c:arg name="count_set">
<c:value-of name="any_set" />
</c:arg>
</c:apply>
<!-- ensure the equation is not undefined if length = 0 -->
<c:const value="1" type="integer" desc="Add 1 to ensure equation is always defined" />
</c:sum>
</c:quotient>
</c:ceil>
</function>
</package>

View File

@ -0,0 +1,540 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="Filtering Vectors and Matrices">
<import package="../base" />
<import package="list" />
<section title="Vector Filtering">
<function name="vfilter_lookup"
desc="Filter predicate by value and use corresponding index in
source vector as a value">
<param name="vector_pred" type="float" set="vector" desc="Vector to filter" />
<param name="vector_src" type="float" set="vector" desc="Vector to filter" />
<param name="value" type="float" desc="Predicate value" />
<param name="start_index" type="integer" desc="Starting vector_pred index" />
<t:cons-until-empty set="vector_pred" only="value" index="start_index">
<c:value-of name="vector_src" index="start_index" />
</t:cons-until-empty>
</function>
</section>
<section title="Matrix Filtering">
\ref{mfilter} handles complex filtering of matrices.
If the requested column~\tt{@col@} is marked as sequential with~\tt{@seq@},
a~$O(lg n)$ bisect algorithm will be used;
otherwise,
it will undergo a~$O(n)$ linear scan.
<function name="mfilter"
desc="Filter matrix rows by column value">
<param name="matrix" type="float" set="matrix" desc="Matrix to filter" />
<param name="col" type="integer" desc="Column index to filter on" />
<param name="vals" type="float" desc="Column value to filter on" />
<param name="seq" type="boolean" desc="Is data sequential?" />
<!-- merge the result of each condition in vals into a single set, which
has the effect of supporting multiple conditions on a single column of
data (or just one, if they don't want to feel sweet). By performing
the lookups separately for each, we preserve the bisect-ability of the
condition. -->
<t:merge-until-empty set="vals" car="val" glance="TABLE_WHEN_MASK_VALUE">
<c:apply name="mrange" matrix="matrix" col="col" val="val" seq="seq">
<c:arg name="start">
<c:cases>
<!-- if we know that the data is sequential, then we may not need to
perform a linear search (if the dataset is large enough and the
column value is relatively distinct) -->
<c:case>
<c:when name="seq">
<c:eq>
<c:value-of name="TRUE" />
</c:eq>
</c:when>
<c:apply name="bisect" matrix="matrix" col="col" val="val">
<c:arg name="start">
<c:const value="0" desc="Start bisect at beginning" />
</c:arg>
<c:arg name="end">
<!-- bisect the length of the matrix -->
<t:dec>
<c:length-of>
<c:value-of name="matrix" />
</c:length-of>
</t:dec>
</c:arg>
</c:apply>
</c:case>
<!-- we have no good guess; linear search :x -->
<c:otherwise>
<c:const value="0" desc="Start at the first element" />
</c:otherwise>
</c:cases>
</c:arg>
<c:arg name="end">
<t:dec>
<c:length-of>
<c:value-of name="matrix" />
</c:length-of>
</t:dec>
</c:arg>
</c:apply>
</t:merge-until-empty>
</function>
<function name="mrange"
desc="Filter matrix rows by column value within a certain
range of indexes (inclusive)">
<param name="matrix" type="float" set="matrix" desc="Matrix to filter" />
<param name="col" type="integer" desc="Column index to filter on" />
<param name="val" type="float" desc="Column value to filter on" />
<param name="start" type="integer" desc="Starting index (inclusive)" />
<param name="end" type="integer" desc="Ending index (inclusive)" />
<param name="seq" type="boolean" desc="Is data sequential?" />
<c:let>
<c:values>
<c:value name="curval" type="float" desc="Current value">
<c:value-of name="matrix">
<c:index>
<c:value-of name="start" />
</c:index>
<c:index>
<c:value-of name="col" />
</c:index>
</c:value-of>
</c:value>
</c:values>
<!-- nested let needed so that the curval is available to over
in the body below -->
<c:let>
<c:values>
<!-- determine if the value we're looking for is over the current value
in a sorted list (meaning that we will not find it) -->
<c:value name="over" type="boolean"
desc="Did we pass the potential value in a sorted list?">
<c:value-of name="TRUE">
<c:when name="seq">
<c:eq>
<c:value-of name="TRUE" />
</c:eq>
</c:when>
<c:when name="curval">
<c:gt>
<c:value-of name="val" />
</c:gt>
</c:when>
</c:value-of>
</c:value>
</c:values>
<c:cases>
<!-- if we're done filtering, then return an empty set -->
<c:case>
<c:when name="start">
<c:gt>
<c:value-of name="end" />
</c:gt>
</c:when>
<!-- empty set -->
<c:set />
</c:case>
<!-- if the data is sequential and the next element is over the
requested value, then we're done -->
<c:case>
<c:when name="over">
<c:eq>
<c:value-of name="TRUE" />
</c:eq>
</c:when>
<!-- empty set -->
<c:set />
</c:case>
<c:otherwise>
<c:apply name="_mfilter" matrix="matrix" col="col" val="val"
start="start" end="end" seq="seq">
<c:arg name="cur">
<c:value-of name="matrix">
<!-- current row -->
<c:index>
<c:value-of name="start" />
</c:index>
<!-- requested column -->
<c:index>
<c:value-of name="col" />
</c:index>
</c:value-of>
</c:arg>
</c:apply>
</c:otherwise>
</c:cases>
</c:let>
</c:let>
</function>
<function name="_mfilter" desc="mfilter helper">
<param name="matrix" type="float" set="matrix" desc="Matrix to filter" />
<param name="col" type="integer" desc="Column index to filter on" />
<param name="val" type="float" desc="Column value to filter on" />
<param name="start" type="integer" desc="Starting index (aka current index)" />
<param name="end" type="integer" desc="Ending index" />
<param name="seq" type="integer" desc="Is data sequential?" />
<param name="cur" type="float" desc="Current value" />
<c:cases>
<c:case>
<c:when name="cur">
<c:eq>
<c:value-of name="val" />
</c:eq>
</c:when>
<c:cons>
<c:value-of name="matrix">
<c:index>
<c:value-of name="start" />
</c:index>
</c:value-of>
<c:apply name="mrange" matrix="matrix" col="col" val="val"
end="end" seq="seq">
<c:arg name="start">
<c:sum>
<c:value-of name="start" />
<c:const value="1" desc="Check next element" />
</c:sum>
</c:arg>
</c:apply>
</c:cons>
</c:case>
<c:otherwise>
<c:apply name="mrange" matrix="matrix" col="col" val="val"
end="end" seq="seq">
<c:arg name="start">
<c:sum>
<c:value-of name="start" />
<c:const value="1" desc="Check next element" />
</c:sum>
</c:arg>
</c:apply>
</c:otherwise>
</c:cases>
</function>
<section title="Bisecting">
Perform an~$O(lg n)$ bisect on a data set.
This is intended to limit recursion on very large data sets (and
consequently will increase performance).
This will bisect up until a certain point (the gap),
unless it finds the value in question.
After finding the value,
it will perform an~$O(n)$ linear backward search to find the first
occurrence of the value.
If the value is not found,
it will halt at the gap and return the first index of the gap,
which we will consider its "best guess",
at which point a linear search can be performed by the caller to
determine if the value does in fact exist at all.
(The reason this operates so oddly is because of its caller;
we could rid the gap and make this a general-purpose function if need be.
Technically,
the gap is useless and saves $lg g$ steps,
which may be very small.)
<const name="TABLE_WHEN_MASK_VALUE" type="float" value="-0.0000001"
desc="Used to mask when conditions" />
<const name="MFILTER_BISECT_GAP_MAX" type="integer" value="10"
desc="Quit bisect if size is less than or equal to this value" />
<function name="bisect"
desc="Bisect a matrix toward the requested column value">
<param name="matrix" type="float" set="matrix" desc="Matrix to bisect" />
<param name="col" type="integer" desc="Column index to filter on" />
<param name="val" type="float" desc="Column value to filter on" />
<param name="start" type="integer" desc="Start index" />
<param name="end" type="integer" desc="Start end" />
<c:let>
<c:values>
<!-- the gap represents the number of indexes between the current start
and end indexes -->
<c:value name="gap" type="integer" desc="Gap between current start and end">
<c:sum>
<c:value-of name="end" />
<t:negate>
<c:value-of name="start" />
</t:negate>
</c:sum>
</c:value>
</c:values>
<!--
At this point, we need to determine if we should continue the bisect or
halt. The purpose of the gap is based on the understanding that (with
our use cases) we will arrive, almost always, at one of two scenarios:
we found the value, but it's part of a larger set of the same values, or
the value we are looking for may not even exist at all.
The gap just limits recursion (but just barely) at smaller levels, since
bisect will take lg(n) steps). Increase the gap limit to decrease the
number of steps, or decrease it to 1 if you want a complete bisection.
-->
<c:cases>
<!-- give up if we've reached our gap limit -->
<c:case>
<c:when name="gap">
<c:lte>
<c:value-of name="MFILTER_BISECT_GAP_MAX" />
</c:lte>
</c:when>
<!-- we tried our best; return our current position -->
<c:value-of name="start" />
</c:case>
<!-- we have not yet reached our gap limit; keep going -->
<c:otherwise>
<c:let>
<c:values>
<c:value name="mid_index" type="integer" desc="Middle index">
<!-- to determine the new mid index, add half of the gap to the
current index -->
<c:sum>
<c:value-of name="start" />
<c:ceil>
<c:quotient>
<c:value-of name="gap" />
<c:const value="2" desc="Bisect" />
</c:quotient>
</c:ceil>
</c:sum>
</c:value>
</c:values>
<c:let>
<c:values>
<c:value name="mid" type="float" desc="Middle value">
<c:value-of name="matrix">
<!-- row -->
<c:index>
<c:value-of name="mid_index" />
</c:index>
<!-- column -->
<c:index>
<c:value-of name="col" />
</c:index>
</c:value-of>
</c:value>
</c:values>
<c:cases>
<!-- if the middle value is lower than our value, then take the upper half -->
<c:case>
<c:when name="mid">
<c:lt>
<c:value-of name="val" />
</c:lt>
</c:when>
<c:recurse start="mid_index" />
</c:case>
<!-- similarily, take the lower half if we over-shot -->
<c:case>
<c:when name="mid">
<c:gt>
<c:value-of name="val" />
</c:gt>
</c:when>
<c:recurse end="mid_index" />
</c:case>
<!-- if we have an exact match, that doesn't necessarily mean that we
have every value; we may have intersected a set of them -->
<c:otherwise>
<!-- this will return an exact index: the first index
containing the element we've been looking for (it is a
linear backwards search) -->
<c:apply name="foremost" matrix="matrix" col="col" i="mid_index" />
</c:otherwise>
</c:cases>
</c:let>
</c:let>
</c:otherwise>
</c:cases>
</c:let>
</function>
<function name="foremost"
desc="Search backwards for the first occurrance in a sorted list">
<param name="matrix" type="float" set="matrix" desc="Matrix to bisect" />
<param name="col" type="integer" desc="Column index to search on" />
<param name="i" type="integer" desc="Current index" />
<c:let>
<c:values>
<!-- we calculate this rather than accept it via an argument so that
this function may be called directly in a more convenient manner
-->
<c:value name="val" type="float" desc="Current value">
<c:value-of name="matrix">
<!-- row -->
<c:index>
<c:value-of name="i" />
</c:index>
<!-- column -->
<c:index>
<c:value-of name="col" />
</c:index>
</c:value-of>
</c:value>
<c:value name="prev" type="float" desc="Previous value">
<c:value-of name="matrix">
<!-- row -->
<c:index>
<t:dec>
<c:value-of name="i" />
</t:dec>
</c:index>
<!-- column -->
<c:index>
<c:value-of name="col" />
</c:index>
</c:value-of>
</c:value>
</c:values>
<c:cases>
<!-- if we have no more indexes to check, then we're done -->
<c:case>
<c:when name="i">
<c:eq>
<c:const value="0"
desc="Did we check the final (first) index?" />
</c:eq>
</c:when>
<!-- well, then, we're done -->
<c:value-of name="i" />
</c:case>
<!-- if the previous column value is the same value, then continue checking -->
<c:case>
<c:when name="prev">
<c:eq>
<c:value-of name="val" />
</c:eq>
</c:when>
<c:recurse>
<c:arg name="i">
<t:dec>
<c:value-of name="i" />
</t:dec>
</c:arg>
</c:recurse>
</c:case>
<!-- otherwise, we've found the foremost index -->
<c:otherwise>
<c:value-of name="i" />
</c:otherwise>
</c:cases>
</c:let>
</function>
<template name="_mask-unless_"
desc="Mask a value unless the condition is truthful">
<param name="@values@" desc="Body" />
<param name="@name@" desc="Scalar to check" />
<param name="@index@" desc="Optional index" />
<param name="@desc@" desc="Optional description" />
<c:cases>
<!-- if masked -->
<c:case>
<!-- no index provided -->
<unless name="@index@">
<c:when name="@name@">
<c:eq>
<c:value-of name="FALSE" />
</c:eq>
</c:when>
</unless>
<!-- index provided -->
<if name="@index@">
<c:when name="@name@" index="@index@">
<c:eq>
<c:value-of name="FALSE" />
</c:eq>
</c:when>
</if>
<!-- TODO: configurable mask via meta and/or param -->
<c:value-of name="TABLE_WHEN_MASK_VALUE" />
</c:case>
<!-- if not masked -->
<c:otherwise>
<param-copy name="@values@" />
</c:otherwise>
</c:cases>
</template>
</section>
</section>
</package>

View File

@ -0,0 +1,131 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="Folding and Unfolding of Vectors">
<import package="../base" />
<import package="filter" export="true" />
The term ``fold'' is also referred to as ``reduce''---%
they are synonymous.
Unless otherwise specified,
folding occurs left-to-right.
\emph{Unfolding} is the opposite of a reduction---%
it \emph{generates} values from existing values.
<section title="Matrix Folding">
\ref{_fold-matrix_} folds a matrix into a vector by summing each of
the~rows.
<template name="_fold-matrix_"
desc="Folds matrix into a vector by summing rows">
<param name="@name@" desc="Matrix to fold" />
<param name="@class@" desc="Iteration vector of desired length" />
<param name="@generates@" desc="Generator name (to yield)" />
<param name="@yields@" desc="Yield name">
<text></text>
</param>
<rate-each class="@class@" yields="@yields@"
generates="@generates@" index="k">
<c:let>
<c:values>
<c:value name="row" type="float" set="vector"
desc="Matrix row">
<c:value-of name="@name@" index="k" />
</c:value>
</c:values>
<c:sum of="row" />
</c:let>
</rate-each>
</template>
</section>
<section title="Matrix Generation">
\ref{_unfold-vector-grouped_} generates a matrix from a vector---%
that is, it generates vectors within a vector---%
by grouping values.
The \tt{@class@} is used both as a predicate and as a determination of
the resulting vector's length
(the~number of rows in the resulting matrix).
If non-matching,
no columns will be produced for that respective row.
\tt{@src@} is the vector to be unfolded,
containing the raw values to be grouped.
\tt{@grouping@} \should be the same length as~\ref{@src@} and determines
the group~(row) in which the respective value should appear.
\ref{@generates@} names the resulting matrix and~\ref{@desc@} provides
its description.
<template name="_unfold-vector-grouped_"
desc="Unfold vector into a matrix by grouping">
<param name="@class@" desc="Iteration vector of desired length" />
<param name="@src@" desc="Source vector" />
<param name="@grouping@" desc="Grouping vector" />
<param name="@generates@" desc="Generator name (to yield)" />
<param name="@desc@" desc="Generator description">
<text>Unfolded vector </text>
<param-value name="@src@" />
<text> grouped by </text>
<param-value name="@grouping@" />
</param>
<param name="@lengthv@" desc="Length vector (of desired length)">
<param-class-to-yields name="@class@" />
</param>
<rate yields="_{@generates@}">
<c:sum of="@lengthv@" dim="matrix"
generates="@generates@" index="k"
desc="@desc@">
<c:cases>
<c:case label="Unfold on class vector match">
<c:when name="@lengthv@" index="k" />
<c:apply name="vfilter_lookup"
vector_pred="@grouping@" vector_src="@src@"
value="k"
start_index="#0" />
</c:case>
<c:otherwise label="Ignore on class vector non-match">
<c:set />
</c:otherwise>
</c:cases>
</c:sum>
</rate>
</template>
</section>
</package>

View File

@ -0,0 +1,402 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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:set 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:set>
</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:when field="@key@">
<c:value-of name="low" />
<c:value-of name="high" />
</t:when>
<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>

View File

@ -0,0 +1,137 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2017 R-T Specialty, 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="Operations Based on Vector Length">
<import package="../base" />
<import package="../numeric/common" export="true" />
See the respective test specification for examples.
<section title="Non-empty Vectors">
\ref{_first-nonempty_} will yield the result of the first toplevel
expression that is not an empty vector.
This template yields an empty vector if no non-empty vectors are found.
<template name="_first-nonempty_"
desc="Return the first non-empty vector">
<param name="@values@" desc="List of vectors" />
<c:let>
<c:values>
<!-- avoid having to copy @values@ multiple times -->
<c:value name="_list" type="float"
desc="Result of body expression">
<c:set>
<param-copy name="@values@" />
</c:set>
</c:value>
</c:values>
<c:apply name="_first_nonempty">
<c:arg name="list">
<c:value-of name="_list" />
</c:arg>
<c:arg name="index">
<c:const value="0" desc="First element" />
</c:arg>
<c:arg name="length">
<c:length-of>
<c:value-of name="_list" />
</c:length-of>
</c:arg>
</c:apply>
</c:let>
</template>
Its helper function is \ref{_first_nonempty},
which recurses through each vector element until a non-empty vector is
encountered.
<function name="_first_nonempty"
desc="Return the first non-empty vector">
<param name="list" type="float" set="vector"
desc="List of vectors to process" />
<param name="index" type="integer"
desc="Current index (for recursion), decrementing" />
<param name="length" type="integer"
desc="Length of list" />
<c:let>
<c:values>
<c:value name="cur_len" type="integer"
desc="Length of current vector">
<c:length-of>
<c:value-of name="list" index="index" />
</c:length-of>
</c:value>
</c:values>
<c:cases>
<!-- if none were found (end of list), return empty vector -->
<c:case>
<c:when name="index">
<c:gte>
<c:value-of name="length" />
</c:gte>
</c:when>
<c:set />
</c:case>
<!-- non-empty vector, return it -->
<c:case>
<c:when name="cur_len">
<c:gt>
<c:const value="0" desc="Vector length" />
</c:gt>
</c:when>
<c:value-of name="list" index="index" />
</c:case>
<!-- vector was empty, keep going (decrement index) -->
<c:otherwise>
<c:recurse>
<c:arg name="index">
<t:inc>
<c:value-of name="index" />
</t:inc>
</c:arg>
</c:recurse>
</c:otherwise>
</c:cases>
</c:let>
</function>
</section>
</package>

View File

@ -0,0 +1,255 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015, 2017 R-T Specialty, 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="Treating vectors as lists">
<import package="../base" />
<import package="../numeric/common" export="true" />
<import package="../when" export="true" />
<!--
This abstraction relieves the developer of one of the most common
lisp-style recursions: recursing over a set's cdr until empty.
This template recurses and, as such, should only be used within functions.
-->
<template name="_cons-until-empty_" desc="Generate empty base case for functions that recurse on cdrs of sets">
<param name="@values@" desc="Body" />
<param name="@set@" desc="Set to operate on" />
<param name="@car@" desc="Variable in which to store car of the set">
<text>__car</text>
</param>
<param name="@cdr@" desc="Variable in which to store the cdr of the set">
<text>__cdr</text>
</param>
<param name="@index@" desc="Index variable" />
<param name="@base@" desc="Base set to return (otherwise an empty set)" />
<param name="@glance@" desc="Glance at (but do nothing with) this value; recurse without action" />
<param name="@only@" desc="Process only this value; otherwise recurse without action" />
<!-- intended for use by merge-until-empty to reduce duplicate code; not to
be set via direct template applications -->
<param name="@merge@" desc="Perform merge instead of cons; system-use only" />
<c:let>
<c:values>
<!-- TODO: it'd be nice if the DSL made values unique for us, unless
they're used, to prevent potential conflicts with template callers
-->
<c:value name="__valn" type="integer" desc="Number of values">
<c:length-of>
<c:value-of name="@set@" />
</c:length-of>
</c:value>
</c:values>
<c:cases>
<c:case>
<c:when name="__valn">
<c:eq>
<c:const value="0" type="integer" desc="When there are no more elements in the set" />
</c:eq>
</c:when>
<!-- if a base set was provided, return that; otherwise, return an empty set -->
<if name="@base@">
<c:value-of name="@base@" />
</if>
<unless name="@base@">
<!-- return an empty set -->
<c:set />
</unless>
</c:case>
<c:otherwise>
<c:let>
<c:values>
<c:value name="@car@" type="float" desc="Car of set">
<c:car>
<c:value-of name="@set@" />
</c:car>
</c:value>
<c:value name="@cdr@" type="float" desc="Cdr of set">
<c:cdr>
<c:value-of name="@set@" />
</c:cdr>
</c:value>
</c:values>
<!-- this case statement will be optimized away if we have no
@glance@ or @only@ value -->
<c:cases>
<!-- if we have a glancing value, then immediately recurse
without processing if we have a match -->
<if name="@glance@">
<c:case>
<t:when-eq name="@car@" value="@glance@" />
<c:recurse>
<c:arg name="@set@">
<c:value-of name="@cdr@" />
</c:arg>
<if name="@index@">
<c:arg name="@index@">
<t:inc>
<c:value-of name="@index@" />
</t:inc>
</c:arg>
</if>
</c:recurse>
</c:case>
</if>
<!-- if we should only recurse when a value matches, ignore
non-match -->
<if name="@only@">
<c:case>
<t:when-ne name="@car@" value="@only@" />
<c:recurse>
<c:arg name="@set@">
<c:value-of name="@cdr@" />
</c:arg>
<if name="@index@">
<c:arg name="@index@">
<t:inc>
<c:value-of name="@index@" />
</t:inc>
</c:arg>
</if>
</c:recurse>
</c:case>
</if>
<!-- otherwise, process as normal -->
<c:otherwise>
<unless name="@merge@">
<!-- here's our recursive operation: cons the result of processing
this car with the result of recursively processing the cdr
(note that c:recurse will recurse on the function that applied
this template, _not_ on the template itself)-->
<c:cons>
<param-copy name="@values@" />
<c:recurse>
<c:arg name="@set@">
<c:value-of name="@cdr@" />
</c:arg>
<if name="@index@">
<c:arg name="@index@">
<t:inc>
<c:value-of name="@index@" />
</t:inc>
</c:arg>
</if>
</c:recurse>
</c:cons>
</unless>
<if name="@merge@">
<!-- the order is different from the cons above to maintain
consistency in the returned set -->
<c:apply name="vmerge">
<c:arg name="vector">
<c:recurse>
<c:arg name="@set@">
<c:value-of name="@cdr@" />
</c:arg>
<if name="@index@">
<c:arg name="@index@">
<t:inc>
<c:value-of name="@index@" />
</t:inc>
</c:arg>
</if>
</c:recurse>
</c:arg>
<c:arg name="onto">
<param-copy name="@values@" />
</c:arg>
</c:apply>
</if>
</c:otherwise>
</c:cases>
</c:let>
</c:otherwise>
</c:cases>
</c:let>
</template>
<!--
Like cons-until-empty, except that it merges each application instead of cons-ing it.
This template recurses and, as such, should only be used within functions.
-->
<template name="_merge-until-empty_" desc="Generate empty base case for functions that recursively merge on cdrs of sets">
<param name="@values@" desc="Body" />
<param name="@set@" desc="Set to operate on" />
<param name="@car@" desc="Variable in which to store car of the set" />
<param name="@cdr@" desc="Variable in which to store the cdr of the set">
<text>__cdr</text>
</param>
<param name="@glance@" desc="Glance at (but do nothing with) this value; recurse without action" />
<param name="@only@" desc="Process only this value; otherwise recurse without action" />
<!-- to reduce duplicate template code, we simply set a merge flag on cons-until-empty -->
<t:cons-until-empty set="@set@" car="@car@" cdr="@cdr@"
glance="@glance@" only="@only@" merge="true">
<param-copy name="@values@" />
</t:cons-until-empty>
</template>
<!--
Merges two vectors
-->
<function name="vmerge" desc="Merge two vectors (does not remove duplicates)">
<param name="vector" type="float" set="vector" desc="Vector to merge" />
<param name="onto" type="float" set="vector" desc="Vector to append to" />
<!-- the template handles much of this for us: just keep cons'ing the car
until we have nothing more to cons, then that gets cons'd onto the
base -->
<t:cons-until-empty set="vector" car="car" base="onto">
<c:value-of name="car" />
</t:cons-until-empty>
</function>
</package>

View File

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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="Matrix (vector of vectors) arithmetic">
<import package="../base" />
<import package="list" />
<!--
Extracts a vector of rates (a value for each location) associated with the
requested class and line codes
This seems more complicated/confusing than it really is, so it helps to
look at an example. Consider that we have the following data passed to this
template (assuming that this is the data that will exist at runtime):
R = [ [ "A", "B" ], [ "C" ], [ "D", "E" ] ]
C = [ [ 1 , 0 ], [ 0 ], [ 0, 1 ] ]
L = "gl"
Given matrix R (@rate_matrix@) and the classification matches C, for any
index that matches the given line L, a dot product will be performed. Let
us assume that indexes 0 and 2 match GL; we would then have the following
dot product:
R = [ [ "A", "B" ], ..., [ "D", "E" ] ]
C = [ [ 1 , 0 ], ..., [ 0, 1 ] ]
= [ "A", "E" ]
And so the resulting vector would be [ "A", "E" ]. To put this into an
example within the context of dwelling, let C above be the classification
set for all vacant buildings. Then, [ "A", "E" ] would be the rate
associated with the vacant building class code for locations 0 and 2
respectively.
If multiple matches are found per location, then the two rates will be
summed (as that is how dot products work), which is obviously not good.
However, this should not be a problem, because you should not have
duplicate class codes per location. Fix your data if that happens.
(Note that the dot product is actually performed for each index and then
multiplied by the classification match __CMATCH__, so while the effect
above is true, the result is achieved in a slightly different manner.)
-->
<!-- TODO: more generic name and documentation; we are in the core, after all -->
<template name="_rate-vector_" desc="Matches against a matrix of classes and a matrix of rates and returns a vector representing the rates per location">
<param name="@line@" desc="Line code classification string" />
<param name="@class_set@" desc="Matrix containing line classification matches" />
<param name="@into@" desc="Variable to which the vector will be assigned" />
<!-- this would be the sum of all the rates, which really is useless, but the XML requires it -->
<param name="@yields@" desc="Variable to yield to (useless, but required)">
<text>__</text>
<param-value name="@into@" />
</param>
<param name="@rate_matrix@" desc="Matrix containing the rates for the given line">
<text>rate_</text>
<param-value name="@line@" />
</param>
<rate-each class="@line@" accumulate="none" yields="@yields@" generates="@into@" index="k">
<!-- take the dot product of the two vectors (each part of a larger matrix)
to get the rate for the associated class code -->
<c:product dot="true" label="Dot product between the class and rate vectors for each location will yield the respective rate per location">
<c:value-of name="@class_set@" index="k" />
<c:value-of name="@rate_matrix@" index="k" />
</c:product>
</rate-each>
</template>
<function name="mcol" desc="Retrieve a matrix column as a vector">
<param name="matrix" type="float" set="matrix" desc="Matrix" />
<param name="col" type="integer" desc="Column index (0-indexed)" />
<!-- generate a vector containing only the requested column from each row -->
<t:cons-until-empty set="matrix" car="row">
<c:value-of name="row">
<c:index>
<c:value-of name="col" />
</c:index>
</c:value-of>
</t:cons-until-empty>
</function>
</package>

View File

@ -0,0 +1,236 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015 R-T Specialty, 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"
title="Maximum and Minimum Elements">
<import package="../base" />
<import package="../numeric/common" />
<import package="../numeric/minmax" />
<section title="Vector Reduction">
Core currently only offers a~maximum reduction on
a~vector. \todo{Add a~minimum reduction.} \ref{_maxreduce_}
provides a convenient template-based abstraction.
<template name="_maxreduce_"
desc="Reduce a set to its maximum">
<param name="@values@" desc="Values to reduce" />
<param name="@isvector@" desc="Set to 'true' if the nodes should
not be wrapped in c:set" />
<param name="@label@" desc="Application label">
<!-- default empty -->
<text></text>
</param>
<c:apply name="maxreduce" label="@label@">
<c:arg name="maxreduce_set">
<!-- if we were not provided with a vector (default), create
one out of the given nodes -->
<unless name="@isvector@" eq="true">
<c:set>
<param-copy name="@values@" />
</c:set>
</unless>
<!-- if they told us that they have provided a vector, then
do not create one -->
<if name="@isvector@" eq="true">
<param-copy name="@values@" />
</if>
</c:arg>
</c:apply>
</template>
\ref{maxreduce} is the~function encapsulated by the template and
may be applied directly.
\todo{This function was originally written before TAME supported
let~expressions and other convenience templates. It has since
been refactored slightly, but can be made to be more concise.}
<function name="maxreduce" desc="Reduce a set to its maximum">
<param name="maxreduce_set" type="float" set="vector"
desc="Set to find max of" />
<c:let>
<c:values>
<c:value name="n" type="integer"
desc="Length of set to reduce">
<c:length-of>
<c:value-of name="maxreduce_set" />
</c:length-of>
</c:value>
</c:values>
<c:cases>
<!-- if we have no values to reduce, then simply return 0 -->
<c:case>
<c:when name="n">
<c:eq>
<c:const value="0"
desc="When there are no elements" />
</c:eq>
</c:when>
<c:const value="0" type="integer" desc="No value" />
</c:case>
<!-- we have values; perform reduction -->
<c:otherwise>
<c:apply name="_maxreduce">
<c:arg name="_maxreduce_set">
<c:value-of name="maxreduce_set" />
</c:arg>
<c:arg name="_maxreduce_i">
<t:dec>
<c:length-of>
<c:value-of name="maxreduce_set" />
</c:length-of>
</t:dec>
</c:arg>
</c:apply>
</c:otherwise>
</c:cases>
</c:let>
</function>
\ref{_maxreduce} is a~helper function that recursively determines
the maximum value of the given set. It should not be invoked
directly; use \ref{maxreduce} or~\ref{_maxreduce_} instead.
For example, given the vector~\vector{2,4,3,1}, here's the
recursive application:
\begingroup
\def\max{\textrm{max}}
\begin{equation}
\max( 1, \max( 3, max( 4, 2 ) ) )
\\ \yield \max( 1, \max( 3, 4 ) )
\\ \yield \max( 1, 4 )
\\ \yield 4
\end{equation}
\endgroup
\todo{If we eventually support first-class functions, then this
can simply be a~generic reduce function that accepts max/min/etc.}
\todo{This function was written before local variables generated
their own unique symbols; strip the prefixes.}
<function name="_maxreduce"
desc="Recursively reduce a set to its maximum (called by maxreduce)">
<param name="_maxreduce_set" type="float" set="vector"
desc="Set to find max of" />
<param name="_maxreduce_i" type="integer"
desc="Index" />
<c:cases>
<!-- base case: if we're on the last index, do not recurse -->
<c:case>
<c:when name="_maxreduce_i">
<c:eq>
<c:const value="0"
desc="Return when only one element remains" />
</c:eq>
</c:when>
<!-- return the first value -->
<c:value-of name="_maxreduce_set">
<c:index>
<c:const value="0"
desc="First item in set" />
</c:index>
</c:value-of>
</c:case>
<!-- we have more elements in the set; recursively determine
the maximum value -->
<c:otherwise>
<c:apply name="max">
<!-- the first element to compare is our index -->
<c:arg name="max1">
<c:value-of name="_maxreduce_set">
<c:index>
<c:value-of name="_maxreduce_i" />
</c:index>
</c:value-of>
</c:arg>
<!-- and we'll compare to the recursive application of the
same set on the previous index -->
<c:arg name="max2">
<c:apply name="_maxreduce">
<c:arg name="_maxreduce_set">
<c:value-of name="_maxreduce_set" />
</c:arg>
<c:arg name="_maxreduce_i">
<c:sum>
<c:value-of name="_maxreduce_i" />
<c:const value="-1" type="integer" desc="Decrement index by 1" />
</c:sum>
</c:arg>
</c:apply>
</c:arg>
</c:apply>
</c:otherwise>
</c:cases>
</function>
</section>
<!-- TODO: Everything below this line must be moved. This
technically operates on a matrix as well. -->
<!-- simplifies retrieving the max of a set of values -->
<template name="_maxOfEach_" desc="Take the max of the given set of values">
<param name="@class@" desc="Class to match on" />
<param name="@values@" desc="Individual values without set" />
<param name="@generates@" desc="Value to generate into" />
<param name="@index@" desc="Index to use for rate-each" />
<param name="@yields@" desc="Yield variable">
<text>_</text>
<param-value name="@generates@" />
</param>
<rate-each class="@class@" accumulate="none" yields="@yields@" generates="@generates@" index="@index@">
<c:apply name="maxreduce">
<c:arg name="maxreduce_set">
<c:set>
<param-copy name="@values@" />
</c:set>
</c:arg>
</c:apply>
</rate-each>
</template>
</package>

View File

@ -0,0 +1,95 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="Stub Vectors and Classifications">
<import package="../base" />
<import package="../tplgen" export="true" />
Stub vectors and classifications of various lengths---%
as well as the means of producing arbitrary lengths---%
are provided for convenience and testing.
<section title="Stub Vectors">
Ten n-vectors with each element containing the value~$0$ are
provided.
<t:for-each-n start="1" end="10" step="1">
<t:n-vector n="@current_n@" name="NVEC{@current_n@}"
value="0" />
</t:for-each-n>
<section title="Sequence Vectors">
Sequence n-vectors are of length~$n$ and have a sequence of values
from~$0$ to~$n-1$.
<t:for-each-n start="1" end="10" step="1">
<t:n-vector n="@current_n@" name="NVEC{@current_n@}_SEQ" />
</t:for-each-n>
</section>
</section>
<section title="Stub Classifications">
Ten n-classes generated from the respective n-vectors are provided.
<t:for-each-n start="1" end="10" step="1">
<classify as="nclass{@current_n@}" yields="nClass{@current_n@}"
desc="Classification {@current_n@}-vector">
<match on="NVEC{@current_n@}" value="ZERO" />
</classify>
</t:for-each-n>
</section>
<section title="Vector Generation">
\ref{_n-vector_} generates vector constants of length~$n$ where each
element is assigned the provided value (or~$0$ if unspecified).
<template name="_n-vector_"
desc="Generate vector of length n">
<param name="@n@" desc="Number of vector elements" />
<param name="@name@" desc="Resulting vector constant name" />
<param name="@value@"
desc="Value of elements (optional; default n-1)" />
<const name="@name@" desc="{@n@}-vector" set="vector">
<t:for-each-n start="1" end="@n@" step="1">
<if name="@value@">
<item value="@value@" desc="Vector value" />
</if>
<unless name="@value@">
<item value="@current_n_dec@" desc="Vector value" />
</unless>
</t:for-each-n>
</const>
</template>
</section>
</package>

View File

@ -0,0 +1,494 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright (C) 2015, 2018 R-T Specialty, 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="Functions for performing table lookups">
<import package="../base" />
<!-- since templates are inlined, we need to make these symbols available to
avoid terrible confusion -->
<import package="../numeric/common" export="true"/>
<import package="common" export="true" />
<import package="filter" export="true" />
<import package="matrix" export="true" />
<!--
Create a constant table
This definition must appear within a `constants' block.
Permitted children:
- _table_column_+ - Column definitions
- _table_rows_ - Begin table data definition
-->
<template name="_create-table_"
desc="Create an arbitrary table for querying">
<param name="@name@" desc="Table name" />
<param name="@values@" desc="Table definition" />
<param name="@desc@" desc="Table description">
<text></text>
</param>
<param name="@__tid@"
desc="Internal table identifier">
<param-value name="@name@" upper="true" snake="true" />
</param>
<param-copy name="@values@">
<param-meta name="create-table-id" value="@__tid@" />
<param-meta name="create-table-name" value="@name@" />
<param-meta name="create-table-desc" value="@desc@" />
</param-copy>
</template>
<!--
Declare a table column name
A column definition assigns a field name to a column index. If the
column always contains ordered (sequenced) data, then @seq@ should
be set to "true"; this allows a more efficient query strategy to
be used.
If a table is especially large, the first column should be treated
as the index and always be sequenced.
-->
<template name="_table-column_"
desc="Declare name for table column">
<param name="@name@" desc="Column name" />
<param name="@index@" desc="Column index (0-indexed)" />
<param name="@desc@" desc="Column description">
<param-value name="@name@" />
</param>
<!-- use carefully; leave alone unless data is definately sorted,
or query results may be incorrect -->
<param name="@seq@" desc="Column is sorted (sequential)">
<text></text>
</param>
<param name="@__tid@"
desc="Internal table identifier">
<param-inherit meta="create-table-id" />
</param>
<!-- FIXME: this doesn't contain @__tid@ because of a bug in the
preprocessor; this is fixed in the new DSL -->
<param name="@__constname@"
desc="Name of internal constant used for column lookup">
<text>RATE_TABLE_</text>
<param-value name="@name@" upper="true" snake="true" />
</param>
<!-- column index identifier -->
<const name="{@__tid@}_{@__constname@}"
value="@index@" type="integer"
desc="@desc@" />
<!-- column sequential flag to permit query optimizations -->
<if name="@seq@" eq="true">
<const name="{@__tid@}_{@__constname@}_IS_SEQ"
value="1" type="integer"
desc="{@name@} is sequenced" />
</if>
<unless name="@seq@" eq="true">
<const name="{@__tid@}_{@__constname@}_IS_SEQ"
value="0" type="integer"
desc="{@name@} is unordered" />
</unless>
</template>
<!--
Begin table data definition (rows)
Each _table-row_ child inserts a constant row into the table. Note
that all data must be constant.
Use only one of @data@ or children to define rows.
Permitted children:
- _table-row_* - Table row definitions
-->
<template name="_table-rows_"
desc="Begin table data definition">
<param name="@values@" desc="Row definitions" />
<param name="@data@" desc="GNU Octave/MATLAB-style data definition" />
<param name="@__tid@"
desc="Table identifier">
<param-inherit meta="create-table-id" />
</param>
<param name="@__tname@"
desc="Table name as provided by caller">
<param-inherit meta="create-table-name" />
</param>
<param name="@__desc@"
desc="Table description">
<param-inherit meta="create-table-desc" />
</param>
<if name="@data@">
<const name="{@__tid@}_RATE_TABLE"
type="float"
desc="{@__tname@} table; {@__desc@}"
values="@data@" />
</if>
<unless name="@data@">
<const name="{@__tid@}_RATE_TABLE"
type="float"
desc="{@__tname@} table; {@__desc@}">
<param-copy name="@values@" />
</const>
</unless>
</template>
<!--
Define a constant table row
Rows will be inserted into the table in the order in which they
appear. Note that, if the column is marked as sequenced, it is
important that the order is itself sequenced.
All values must be constant.
Permitted children:
_table-value_+ - Row column value
-->
<template name="_table-row_"
desc="Define a constant table row (ordered)">
<param name="@values@" desc="Column values" />
<set desc="Row">
<param-copy name="@values@" />
</set>
</template>
<!--
Set a column value for the parent row
Column value order should match the defined column order. All
values must be constants.
-->
<template name="_table-value_"
desc="Table column value for row (ordered)">
<param name="@const@" desc="Constant column value" />
<item value="@const@" desc="Column value" />
</template>
<template name="_query-first-field_" desc="Return the requested field from the first row returned by a query">
<param name="@table@" desc="Table (matrix) to query" />
<param name="@values@" desc="Query parameters" />
<!-- use either field or name[+index] -->
<param name="@field@" desc="Column to select (variable/constant); do not use with @name@" />
<param name="@name@" desc="Name of field to query (variable/constant); overrides @field@" />
<param name="@index@" desc="Optional index for field name lookup (using @name@)" />
<c:car label="First row of query result">
<t:query-field table="@table@" field="@field@" name="@name@" index="@index@">
<param-copy name="@values@" />
</t:query-field>
</c:car>
</template>
<!--
Query-style syntax for matrix searches
Overhead of this algorithm is minimal. Let us consider that a direct access
to an element in a vector is O(1). Therefore, given a set of p predicates
in a direct matrix access, the time would be O(p).
In the case of this query method, we perform first a bisect and then a
linear search backward to the first element matching the given predicate.
Therefore, per field, the best case average is O(lg n), where n is the
number of rows; this assumes that no backward linear search is required.
Should such a search be required, the worst case per field is O(lg n + m),
where m is the length of the largest subset.
Once again considering p predicates, each predicate P_k where k > 0
(0-indexed) will operate on a subset of the first predicate. As already
discussed, the worst-case scenerio for the length of the subset is m. Since
further queries on this subset are rarely likely to use the bisect
algorithm, we're looking at O(m) time per field. Therefore, the total query
time for the best-case scenerio is still O(lg n) if m is sufficiently small
and O(lg n + pm) if m is sufficiently large.
An important case to note is when m approaches (or is equal to) n; in such
a case, the algorithm degrades to a worst-case linear search of O(pn) and
best case of O(n) if early predicates are sufficiently effective at
reducing the subsets to further predicates: That is, the bisect algorithm
either cannot be used or is ineffective. For large m, this may cause a
stack overflow due to the recursive nature of the algorithm. Is is
therefore important to order the first column of the table such that it is
both sorted and produces small m. Additionally, it is ideal for the first
predicate to query the first field to quickly reduce the size of the set
for the next predicate.
-->
<template name="_query-field_" desc="Return the requested field from rows returned by a query">
<param name="@table@" desc="Table (matrix) to query" />
<param name="@values@" desc="Query parameters" />
<!-- use one or the other -->
<param name="@field@" desc="Column to select (variable/constant); do not use with @name@" />
<param name="@index@" desc="Optional index for field name lookup (using @name@)" />
<!-- by default, if @field@ is provided instead of @name@, the field
constant will be generated (same concept as the 'when' template) -->
<param name="@name@" desc="Name of field to query (variable/constant); overrides @field@">
<!-- convert @table@ to uppercase and snake case (replace - with _) -->
<param-value name="@table@" upper="true" snake="true" />
<text>_RATE_TABLE_</text>
<param-value name="@field@" upper="true" snake="true" />
</param>
<c:apply name="mcol" label="Query result">
<!-- the matrix (vector of rows) returned by the query -->
<c:arg name="matrix">
<t:query-row table="@table@">
<param-copy name="@values@" />
</t:query-row>
</c:arg>
<!-- the field (column) to retrieve; 0-based index -->
<c:arg name="col">
<!-- no index lookup needed -->
<unless name="@index@">
<c:value-of name="@name@" />
</unless>
<!-- index lookup required -->
<if name="@index@">
<c:value-of name="@name@" index="@index@" />
</if>
</c:arg>
</c:apply>
</template>
<template name="_query-row_" desc="Query a table (matrix) for a row (vector) of values">
<param name="@table@" desc="Table (matrix)" />
<param name="@values@" desc="Query parameters" />
<!-- this defaults to a table name constant as generated from the csv2xml
script; either this or @table@ should be used -->
<param name="@matrix@" desc="Matrix to look up from">
<!-- convert @table@ to uppercase and snake case (replace - with _) -->
<param-value name="@table@" upper="true" snake="true" />
<text>_RATE_TABLE</text>
</param>
<c:apply name="_mquery">
<c:arg name="matrix">
<c:value-of name="@matrix@" />
</c:arg>
<c:arg name="criteria">
<c:set>
<param-copy name="@values@">
<param-meta name="table_basename" value="@matrix@" />
</param-copy>
</c:set>
</c:arg>
<c:arg name="i">
<!-- begin with the last predicate (due to the way we'll recurse, it
will be applied *last* -->
<t:dec>
<c:length-of>
<c:set>
<param-copy name="@values@">
<param-meta name="table_basename" value="@matrix@" />
</param-copy>
</c:set>
</c:length-of>
</t:dec>
</c:arg>
</c:apply>
</template>
<template name="_when_" desc="Create field predicate for query definition">
<param name="@id@" desc="Field index" />
<param name="@values@" desc="Field value (provide only one node)" />
<param name="@sequential@" desc="Is data sequential?" />
<!-- @name@ may be provided directly, or @field@ may be used when the
basename is available (set by a query template), giving the illusion of
querying the table columns by name directly (magic!); pure syntatic
sugary goodness -->
<param name="@field@" desc="Field name (to be used with base)" />
<param name="@name@" desc="Field name (as a variable/constant)">
<param-inherit meta="table_basename" />
<text>_</text>
<param-value name="@field@" upper="true" snake="true" />
</param>
<param name="@seqvar@" desc="Var/constant containing whether field is sequential">
<param-inherit meta="table_basename" />
<text>_</text>
<param-value name="@field@" upper="true" snake="true" />
<text>_IS_SEQ</text>
</param>
<c:set label="Conditional for {@field@}">
<!-- the first element will represent the column (field) index -->
<unless name="@name@">
<c:const value="@id@" type="integer" desc="Field index" />
</unless>
<if name="@name@">
<c:value-of name="@name@" />
</if>
<!-- the second element will represent the expected value(s) -->
<c:set>
<param-copy name="@values@" />
</c:set>
<!-- the final element will represent whether or not this field is sequential -->
<if name="@sequential@">
<c:const value="@sequential@" type="boolean" desc="Whether data is sequential" />
</if>
<unless name="@sequential@">
<!-- if a field name was given, we can get the sequential information
that was already generated for us -->
<if name="@field@">
<c:value-of name="@seqvar@" />
</if>
<!-- otherwise, default to non-sequential -->
<unless name="@field@">
<c:value-of name="FALSE" />
</unless>
</unless>
</c:set>
</template>
<!--
These functions make the magic happen
They are hideous. Look away.
-->
<!-- this function is intended to be called by the _query_ template, not directly -->
<function name="_mquery" desc="Query for vectors using a set of column criteria">
<param name="matrix" type="float" set="matrix" desc="Matrix to query" />
<param name="criteria" type="float" set="matrix" desc="Query criteria" />
<param name="i" type="integer" desc="Current criteria index" />
<c:cases>
<c:case>
<c:when name="i">
<c:eq>
<!-- it's important that we allow index 0, since that is a valid
predicate -->
<c:const value="-1" type="integer" desc="We're done." />
</c:eq>
</c:when>
<!-- we're done; stick with the result -->
<c:value-of name="matrix" />
</c:case>
<c:otherwise>
<c:apply name="mfilter">
<!-- matrix to search -->
<c:arg name="matrix">
<!-- >> recursion happens here << -->
<c:apply name="_mquery">
<c:arg name="matrix">
<c:value-of name="matrix" />
</c:arg>
<c:arg name="criteria">
<c:value-of name="criteria" />
</c:arg>
<c:arg name="i">
<t:dec>
<c:value-of name="i" />
</t:dec>
</c:arg>
</c:apply>
</c:arg>
<!-- field (column) -->
<c:arg name="col">
<c:value-of name="criteria">
<c:index>
<c:value-of name="i" />
</c:index>
<c:index>
<c:const value="0" type="integer" desc="Field id" />
</c:index>
</c:value-of>
</c:arg>
<!-- value(s) to search for -->
<c:arg name="vals">
<c:value-of name="criteria">
<c:index>
<c:value-of name="i" />
</c:index>
<c:index>
<c:const value="1" type="integer" desc="Field value" />
</c:index>
</c:value-of>
</c:arg>
<!-- if it's sequential, we can cut down on the search substantially -->
<c:arg name="seq">
<c:value-of name="criteria">
<c:index>
<c:value-of name="i" />
</c:index>
<c:index>
<c:const value="2" type="integer" desc="Sequential flag" />
</c:index>
</c:value-of>
</c:arg>
</c:apply>
</c:otherwise>
</c:cases>
</function>
</package>

76
core/when.xml 100644
View File

@ -0,0 +1,76 @@
<?xml version="1.0"?>
<!--
Copyright (C) 2018 R-T Specialty, 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="When Matching">
<import package="base" />
This package deals with matching in calculations (such as case statements
or values);
for classifications, see \tt{vector/cmatch}.
<inline-template>
<for-each>
<set cmp="eq" />
<set cmp="ne" />
<set cmp="gt" />
<set cmp="gte" />
<set cmp="lt" />
<set cmp="lte" />
</for-each>
<template name="_when-{@cmp@}_" desc="Value predicate {@cmp@}">
<param name="@name@" desc="Name to assert against" />
<param name="@index@" desc="Name index (optional)">
<text></text>
</param>
<!-- former exists only for consistency with cmatch templates and will
produce an error -->
<param name="@const@" desc="Match against constant value" />
<param name="@value@" desc="Match against variable" />
<param name="@value_index@" desc="Value index (optional)">
<text></text>
</param>
<if name="@const@">
<error>
@const@ is deprecated; use @value@ with a #-prefix instead.
</error>
</if>
<c:when name="@name@" index="@index@">
<dyn-node name="c:{@cmp@}">
<c:value-of name="@value@" index="@value_index@" />
</dyn-node>
</c:when>
</template>
</inline-template>
</package>