Commit Graph

153 Commits (9f990e19e924bd4acc2b6618396976d260727c90)

Author SHA1 Message Date
Mike Gerwitz 1ad2fb1dc8 Copyright year update 2022
RSG (Ryan Specialty Group) recently announced a rename to Ryan Specialty (no
"Group"), but I'm not sure if the legal name has been changed yet or not, so
I'll wait on that.
2022-05-03 14:14:29 -04:00
Mike Gerwitz 2ea66f4f97 tame: @encoding="{ISO-8859-1=>utf-8}" for all XML-based files
TAMER rejects this, because we shouldn't be using anything but UTF-8.  My
use of this encoding is ancient, from over a decade ago, that was apparently
just copied around.

DEV-10936
2022-05-02 12:00:42 -04:00
Corey Vollmer d46aebe4bd [DEV-11788] Add upper & lower abbreviation for states 2022-03-31 16:33:45 -04:00
Mike Gerwitz 297b88c3c1 x/0=0 with global flag for new classification system
This was originally my plan with the new classification system, but it was
undone because I had hoped to punt on the somewhat controversial
issue.  Unfortunately, I see no other way.  Here I attempt to summarize the
reasons why, many of which are specific to the design decisions of TAME.

Keep in mind that TAME is a domain-specific language (DSL) for writing
insurance rating systems.  It should act intuitively for our use case, while
still being mathematically sound.

If you still aren't convinced, please see the link at the bottom.

Target Language Semantics (ECMAScript)
--------------------------------------
First: let's establish what happens today.  TAME compiles into ECMAScript,
which uses IEEE 754-2008 floating-point arithmetic.  Here we have:

  x/0 = Infinity,  x > 0;
  x/0 = -Infinity, x < 0;
  0/0 = NaN,       x = 0.

This is immediately problematic: TAME's calculations must produce concrete
real numbers, always.  NaN is not valid in its domain, and Infinity is of no
practical use in our computational model (TAME is build for insurance rating
systems, and one will never have infinite premium).  Put plainly: the
behavior is undefined in TAME when any of these values are yielded by an
expression.

Furthermore, we have _three different possible situations_ depending on
whether the numerator is positive, negative, or zero.  This makes it more
difficult to reason about the behavior of the system, for values we do not
want in the first place.

We then have these issues in ECMAScript:

  Infinity  * 0 = NaN.
  -Infinity * 0 = NaN.
  NaN       * 0 = NaN.

These are of particular concern because of how predicates work in TAME,
which will be discussed further below.  But it is also problematic because
of how it propagates: once you have NaN, you'll always have NaN, unless you
break out of the situation with some control structure that avoids using it
in an expression at all.

Let's now consider predicates:

  NaN  >  0   = false.
  NaN  <  0   = false.
  NaN === 0   = false.
  NaN === NaN = false.

These will be discussed in terms of classification predicates (matches).

We also have issues of serialization:

  JSON.stringify(Infinity) = "null".
  JSON.stringify(NaN)      = "null".

These means that these values are difficult to transfer between systems,
even if we wanted them.

TAME's Predicates
-----------------
TAME has a classification system based on first-order logic, where ⊥ is
represented by 0 and ⊤ is represented by 1.  These classifications are used
as predicates to calculations via the @class attribute of a rate block.  For
example:

  <rate-each class="property" generates="propValue" index="k">
    <c:quotient>
      <c:value-of name="buildingTiv" index="k" />
      <c:value-of name="tivPropDivisor" index="k" />
    </c:quotient>
  </rate>

As can be observed via the Summary Page, this calculation compiles into the
following mathematical expression:

  ∑ₖ(pₖ(tₖ/dₖ)),

that is—the quotient is then multiplied by the value of the `property`
classification, which is a 0 or 1 respectively for that index.

Let's say that tivPropDivisor were defined in this way:

  <rate-each class="property" generates="tivPropDivisor" index="k">
    <!--- ... logic here ...  -->
  </rate>

It does not matter what the logic here is.  Observe that the predicate here
is `property` as well, which means that, if this risk is not a property
risk, then `tivPropDivisor` will be `0`.

Looking back at `propValue`, let's say that we do have a property risk, and
that `buildingTiv` is `[100_000, 200_000]` and `tivPropDivisor` is 1000.  We
then have:

  1(100,000 / 1000) + 1(200,000 / 1000)) = 300.

Consider instead what happens if `property` is 0.  Since we have no property
locations, we have `[0, 0]` as `buildingTiv` and `tivPropDivisor` is 0.

  0(0/0) + 0(0/0)) = 0(NaN + NaN) = NaN.

This is clearly not what was intended.  The predicate is expected to be
_strongly_ zero, as if using an Iverson bracket:

  ((0/0)[0] + (0/0)[0]) = 0.

Of course, one option is to redefine TAME such that we use Iverson's
convention in place of summation, however this is neither necessary nor
desirable given that

  (a) NaN is not valid within the domain of any TAME expression, and
  (b) Summation is elegantly generalized and efficiently computed using
      vector arithmetic and SIMD functions.

That is: there's no use in messing with TAME's computational model for a
valid that should be impossible to represent.

Short-Circuiting Computation
----------------------------
There's another way to look at it, though: that we intended to skip the
computation entirely, and so it doesn't matter what the quotient is.  If the
compiler were smart enough (and maybe one day it will be), it would know
that the predicate of `tivPropDivisor` and `propValue` are the same and so
there is no circumstance under which we would compute `propValue` and have
`tivPropDivisor` be 0.

The problem is: that short-circuiting is employed as an _optimization_, and
is an implementation detail.  Mathematically, the expression is unchanged,
and is still invalid within TAME's domain.  It is unrepresentable, and so
this is not an out.

But let's pretend that it was defined that way, which would yield this:

              { ∑ₖ(pₖ(tₖ/dₖ)),  ∀x∈p(x = 1);
  propValue = <
              { 0,             otherwise.

This is the optimization that is employed, but it's still not mathematically
correct!  What happens if p₀ = 1, but p₁ = 0?  Then we have:

  1(100,000/1000) + 0(0/0) = 100 + NaN = NaN,

but the _intent_ was clearly to have 100 + 0 = 100, and so we return to the
original problem once again.

Classification Predicates and Intent
------------------------------------
Classifications are used as predicates for equations, but classifications
_themselves_ have predicates in the form of _matches_.  Consider, for
example, a classification that may be used in an assertion to prevent
negative premium from being generated:

  <t:assert failure="premBuilding must not be negative for any index">
    <t:match-gte value="premBuilding" value="#0" />
  </t:assert>

Simple enough—the system will fail if the premium for a given building is
below $0.

But what happens if premBuilding is calculated as so?

  <rate-each class="property" yields="premBuildingTotal"
             generates="premBuilding" index="k">
    <c:product>
      <c:value-of name="propValue" index="k" />
      <c:value-of name="propRate" index="k" />
    </c:product>
  </rate-each>

Alas, if `property` is false for any index, then we know that `propValue` is
NaN, and NaN * x = NaN, and so `premBuilding` is NaN.

The above assertion will compile the match into the first-order sentence

  ∀x∈b(x > 0).

Unfortunately, NaN is not greater than, less than, equal to, or any other
sort of thing to 0, and so _this assertion will trigger_.  This causes
practical problems with the `_premium_` template, which has an
`@allow-zero@` argument to permit zero premium.

Consider this real-world case that I found (variables renamed), to avoid a
strawman:

  <t:premium class="loc" round="cent"
             yields="locInitialTotal"
             generates="locInitial" index="k"
             allow-zero="true"
             desc="...">
    <c:value-of name="premAdditional" />

    <c:quotient>
      <c:value-of name="premLoc" index="k" />
      <c:value-of name="premTotal" />
    </c:quotient>
  </t:premium>

This appears to be responsible for splitting up `premAdditional` relative to
the total premium contribution of each location.  It explicitly states that
it wants to permit a zero value.  The intent of this block is clear: a value
of 0 is explicitly permitted and _expected_.

But if `premTotal` is for whatever reason 0—whether it be due to a test
case or some unexpected input—then it'll yield a NaN and make the entire
expression NaN.  Or if `premAdditional` or `premLoc` are tainted by a NaN,
the same result will occur.  The assertion will trigger.  And, indeed, this
is what I'm seeing with test cases against the new classification system.

What about Infinity?  Is it intuitive that, should `propValue` in the
previous example be positive and `propRate` be 0, that we would, rather than
producing a very small value, produce an infinitely large one?  Does that
match intuition?  Remember, this system is a domain-specific language for
_our_ purposes—it is not intended to be used to model infinities.

For example, say we had this submission because the premium exceeds our
authority to write with some carrier:

  <t:submit reason="Premium exceeds authority">
    <t:match-gt name="premBuilding" value="#100k" />
  </t:submit>

If we had

  (100,000 / 0) = ∞,

then this submit reason would trigger.  Surely that was not intended, since
we have `property` as a predicate and `propRate` with the same predicate,
implying that the answer we _actually_ want is 0!  In that case, what we
_probably_ want to trigger is something like

  <rate yields="premFinal">
    <t:maxreduce>
      <c:value-of name="premBuildingTotal" />
      <c:value-of name="#500" />
    </t:maxreduce>
  </rate>,

in order to apply a minimum premium of $500.  But if `premBuildingTotal` is
Infinity, then you won't get that—you'll get Infinity, which is of course
nonsense.

And nevermind -Infinity.

Why Wasn't This a Problem Before?
---------------------------------
So why bring this up now?  Why have we survived a decade without this?

We haven't, really—these bugs have been hidden.  But the old classification
system covered them up; predicates would implicitly treat missing values as
0 by enclosing them in `(x||0)` in the compiled code.  Observe this
ECMAScript code:

  NaN || 0 = 0.

Consequently, the old classification system absorbed bad values and treated
them implicitly as 0.  But that was a bug, and had to be removed; it meant
that missing indexes in classifications would trigger predicates that were
not intended to be triggered, if they matched against 0, or matched against
a value less than some number larger than zero.  (See
`core/test/core/class` for examples.)

The new classification system does not perform such defaulting.  _But it
also does not expect to receive values outside of its valid domain._
Consequently, _NaN and Infinity lead to undefined behavior_, and the
current implementation causes the predicate to match (NaN < 0) and therefore
fail.

The reason for this is because that this implementation is intended to
convey precisely the computation necessary for the classification system, as
formally defined, so that it can be later optimized even further.  Checking
for values outside the domain not only should not be necessary, but it would
prevent such future optimizations.

Furthermore, parameters used to compile into (param||0), to account for
missing values or empty strings.  This changed somewhat recently with
5a816a4701, which pre-cast all inputs and
allowed relaxing many of those casts since they were both wasteful and no
longer necessary.

Given that, for all practical purposes, 0/0=0 in the system <1yr ago.

Infinity, of course, is a different story, since (Infinity||0)=Infinity;
this one has always been a problem.

Let's Just Fail
---------------
Okay, so we cannot have a valid expression, so let's just fail.

We could mean that in two different ways:

  1. Fail at runtime if we divide by 0; or
  2. Fail at compile-time if we _could_ divide by 0.

Both of these have their own challenges.

Let's dismiss #2 right off the bat for now, because until we have TAMER,
that's not really feasible.  We need something today.  We will discuss that
in the future.

For #1—we cannot just throw an error and halt computation, because if the
`canterm` flag passed into the system is `false`, then _computation must
proceed and return all results_.  Terminating classifications are checked
after returning rather than throwing errors.

Since we have to proceed with computation, then the computations have to be
valid, and so we're left with the same problem again—we cannot have
undefined behavior.

One could argue that, okay, we have undefined behavior, but we're going to
fail because of the assertion anyway!  That's potentially defensible, but it
is at the moment undesirable, because we get so many failures.  And,
relative to the section below, it's not clear to me what benefit we get from
that behavior other than making things more difficult for ourselves.

Furthermore, such an assertion would have to be defined for every
calculation that performs a quotient, and would have to set some
intermediate flag in the calculation which would then have to be checked for
after-the-fact.  This muddies the generated calculation, which causes
problems for optimizations, because it requires peering into state of the
calculation that may be hidden or optimized away.

If we decide that calculations must be valid because we cannot fail, and we
have to stick with the domain of calculations, then `x/0` must be
_something_ within that domain.

x/0=0 Makes Sense With the Current System
-----------------------------------------
Let's take a step back.  Consider a developer who is unaware that
NaN/Infinity are permitted in the system—they just know that division by
zero is a bad thing to do because that's what they learned, and they want to
avoid it in their code.

Consider that they started with this:

  <rate-each class="property" generates="propValue" index="k">
    <c:quotient>
      <c:value-of name="buildingTiv" index="k" />
      <c:value-of name="tivPropDivisor" index="k" />
    </c:quotient>
  </rate>

They have inspected the output of `tivPropDivisor` and see that it is
sometimes 0.  They understand that `property` is a predicate for the
calculation, and so reasonably think that they could do something like this:

  <classify as="nonzero-tiv-prop-divisor" ...>
    <t:match-ne on="tivPropDivisor" value="#0" />
  </classify>

and then change the rate-each to

  <rate-each class="property nonzero-tiv-prop-divisor" ...>.

Except that, of course, we know that will have no effect, because a NaN is a
NaN.  This is not intuitive.

So they'd have to do this:

  <rate-each class="property" generates="propValue" index="k">
    <c:cases>
      <c:case>
        <t:when-ne name="tivPropDivisor" value="#0" />

        <c:quotient>
          <c:value-of name="buildingTiv" index="k" />
          <c:value-of name="tivPropDivisor" index="k" />
        </c:quotient>
      </c:case>

      <c:otherwise>
        <c:value-of name="#0" />
      </c:otherwise>
    </c:cases>
  </rate>.

But for what purpose?  What have we gained over simply having x/0=0, which
does this for you?

The reason why this is so unintuitive is because 0 is the default case in
every other part of the system.  If something doesn't match a predicate, the
value becomes 0.  If a value at an index is not defined, it is implicitly
zero.  A non-matching predicate is 0.

This is exploited for reducing values using summation.  So the behavior of
the system with regards to 0 is always on the mind of the developer.  If we
add it in another spot, they would think nothing of it.

It would be nice if it acted as an identity in a monoidic operation,
e.g. as 0 for sums but as 1 for products, but that's not how the system
works at all today.  And indeed such a thing could be introduced using a
special template in place of `c:value-of` that copies the predicates of the
referenced value and does the right thing.

The _danger_, of course, is that this is _not_ how the system as worked, and
so changing the behavior has the risk of breaking something that has relied
on undefined behavior for so long.  This is indeed a risk, but I have taken
some confident in (a) all the test cases for our system pass despite a
significant number of x/0=0 being triggered due to limited inputs, and (b)
these situations are _not correct today_, resulting in `null` in serialized
result data because `JSON.stringify([NaN, Infinity]) === "[null, null]"`.

Given all of that, predictable incorrect behavior is better than undefined
behavior.

So x/0=0 Isn't Bad?
-------------------
No, and it's mathematically sound.  This decision isn't unprecedented—
Coq, Lean, Agda, and other theorem provers define x/0=0.  APL originally
defined x/0=1, but later switched to 0.  Other languages do their own thing
depending on what is right for their particular situation.

Division is normally derived from

  a × a⁻¹ = 1, a ≠ 0.

We're simply not using that definition—when we say "quotient", or use the
`/` symbol, we mean a _different_ function (`div`, in the compiled JS),
where we have an _additional_ axiom that

  a / 0 = 0.

And, similarly,

  0⁻¹ = 0.

So we've taken a _normally undefined_ case and given it a definition.  No
inconsistency arises.

In fact, this makes _sense_ to do, because _this is what we want_.  The
alternative, as mentioned above, is a lot of boilerplate—checking for 0 any
time we want to do division.  Complicating the compiler to check for those
cases.  And so on.  It's easier to simple state that, in TAME, quotients
have this extra convenient feature whereby you don't have to worry about
your denominator being zero because it'll act as though you enclosed it in a
case statement, and because of that, all your code continues to operate in
an intuitive way.

I really recommend reading this blog post regarding the Lean theorem prover:

  https://xenaproject.wordpress.com/2020/07/05/division-by-zero-in-type-theory-a-faq/
2022-02-28 16:27:51 -05:00
Mike Gerwitz 1796753940 core/vector: Remove aggregate package
Like core/numeric, this was to maintain BC and has not been used for many
years (it does not even build).
2022-01-28 12:01:18 -05:00
Mike Gerwitz a300842582 core/build.xml: Remove
This is no longer necessary (and proably never was).  I assume that this was
added when I was trying to get core to build independently.
2022-01-28 12:00:26 -05:00
Mike Gerwitz 40e2472fac core/numeric: Remove aggregate package
This package was originally added long ago when it was split into
multiple.  It is no longer used.
2022-01-28 11:56:06 -05:00
Mike Gerwitz 2e50af1220 Copyright year update 2021 2021-07-22 15:00:15 -04:00
Mike Gerwitz 6f2b4090cd Correct behavior of matrix matching with separate index sets in new system
This behavior was largely correct, but was not commutative if the size of
the matrices (rows or columns) was smaller than a following match.
2021-06-23 11:44:36 -04:00
Mike Gerwitz 934824b2ee Reintroduce legacy classification system, place new behind flag
This largely reintroduces the legacy classification system, but there are a
number of things that are not affected by the flag.  For example:

  1. Alias classifications are still optimized when the flag is off;
  2. Classifications without predicates emit slightly different code than
     before, though their functionality has not changed;
  3. There's been a lot of refactoring and minor optimizations that are
     unaffected by the flag;
  4. lv:match/@pattern will now emit a warning; and
  5. Cleaning and casting of input data is not gated.

This allows us to incrementally migrate to the new system where behavior may
be different, but this is admittedly a bit dangerous in that the new system
was aggressively tested and reasoned about, so reintroducing the legacy
system may combine in unexpected ways.
2021-06-23 11:44:36 -04:00
Mike Gerwitz cdb2e876ab core/test/class: Begin classification system test cases
These are incomplete, but a start.
2021-06-09 13:33:11 -04:00
Mike Gerwitz 1e620e1e96 core/base (_use-new-classification-system): New template
This template prepares for the introduction of the new classification
system, which is a full rewrite that is both more performant and more
correct in its behavior.  Unfortunately, the corrections will cause problems
with old code that may be relying on certain cases, particularly where
undefined values are implicitly treated as zero.

Consequently, the legacy and new systems will exist side-by-side, able to be
toggled on as desired so people can verify that behavior is correct before
we switch it on by default.  This template allows switching on the system
for an entire package (if it's placed at the toplevel), or portions of a
package, though the latter should only be used in exceptional circumstances.

See the test cases in commits to follow for more information.
2021-06-09 13:32:46 -04:00
Mike Gerwitz d4dc1e651b core/base: Section _yield_ and _rate-each_ 2021-06-08 13:26:49 -04:00
Mike Gerwitz bf399c0370 core/aggregate: Remove package
This package is not used today.  See RELEASES.md for more information;  This
is a dangerous package that never should have existed.

This also fixes the test suite.
2021-06-08 12:00:45 -04:00
Mike Gerwitz 8ce217f779 [DEV-8947] Make mrange fully tail-recursive and enable TCO
We were still having issues with this function when taking the positive
branch, when predicates cause many matches within tables.  This was causing
us to hit stack limits in certain browsers on the Summary Page.

This converts it to an iterator so that all branches are tail-recursive, and
then enables TCO on them.

I was disappointed to find that there's little performance or memory benefit
in running our test suite.
2020-12-09 09:56:43 -05:00
Austin Schaffer d8651cfb95 [DEV-8081] Stop using accumulate in tdat template 2020-08-20 18:18:46 -04:00
Mike Gerwitz 61aec5f714 [DEV-8130] core: mrange: Use experimental guided TCO
This alleviates stack exhaustion issues with large rate tables where the
predicates fail to reduce the search space to a reasonable size.
2020-07-15 10:33:05 -04:00
Mike Gerwitz 3418023269 core: mrange: Inline _mrange_cmp
This will permit the use of TCO in the following commit.
2020-07-15 10:33:05 -04:00
Joseph Frazer 15f5867508 [DEV-7198] Replace `rate-each` macro with a template
Replacing the existing macros with templates will allow us to now have
to deal with macros in the new compiler.

The `indexNameType` pattern needed to change to allow for variables. I
also had to remove the prefix for the `gentle-no` option of `rate`.
2020-04-17 11:35:10 -04:00
Joseph Frazer aa2bc6eedf [DEV-7198] Create a "yield" template
Create a "yield" and add backwards compatibility for the macro of the
same name. This is one of 2 macros that need to be replaced so we do not
have to worry about them with the new compiler.
2020-04-17 07:42:09 -04:00
Joseph Frazer f6bf042505 [DEV-7136] Add xmli files
Add a new step to the build process that copies the `xml` file to an
`xmli` file. Eventually, the new compiler will create the `xmli` file
and the old compiler will convert it to an `amle` file during the
transition.
2020-04-08 08:27:47 -04:00
Mike Gerwitz 8de174d6a2 [DEV-7087] core: Fix extern dim defaults 2020-03-26 09:08:13 -04:00
Mike Gerwitz ee077e8f12 [DEV-7087] core/retry (__retry): dim=0
Now that we will be doing extern type checks, this
must be properly set.
2020-03-26 09:08:13 -04:00
Austin Schaffer 433fc01e77 [DEV-7160] Do not allow terminating classifications for test runner 2020-03-12 13:00:33 -04:00
Austin Schaffer 940d41817f [DEV-7160] Set neg-yields param in retry template 2020-03-12 13:00:33 -04:00
Mike Gerwitz bfea768f89 Copyright year 2020 update 2020-03-06 11:05:18 -05:00
Austin Schaffer e1076ce388 [DEV-6306] Add retry template
[DEV-6306] Add testing instructions to README.md

[DEV-6306] Add assertion to retry template
2020-02-20 08:36:39 -05:00
Joseph Frazer 9f02781a5b [DEV-6947] Add template to match UI values
The UI values need to match AND the question needs to be
visible. We do not have the visibility classifications yet, so we need to
define externs to allow this to build.
2020-01-31 16:27:04 -05:00
Mike Gerwitz d96f090337 core/ui: Correct vector/cmatch import path 2019-11-27 09:17:04 -05:00
Mike Gerwitz 695077d27b core/states.xml: Remove old transition file
Everything should use core/state.
2019-11-27 09:16:47 -05:00
Mike Gerwitz e97f7a75c9 core/test/vector (_define-vector_): Require description
We want things to require documentation when possible.
2019-10-17 09:20:15 -04:00
Mike Gerwitz 39c7161cca vector/define: New package introducing _define-vector_
This is intended to work around the issue of defining arbitrary vectors
outside of a c:let.
2019-10-17 09:16:45 -04:00
Mike Gerwitz ce0e31032b core: ui: Add _match-ui-*_ templates
These are analaogus to the _match-*_ counterparts, but they convert @on@
into the question param and check against the applicability of the question.
2019-08-13 16:46:06 -04:00
Mike Gerwitz a58243c403 core/ui (_match-ui-applicable_): Account for applicability
It doesn't makes sense to consider a question to be set if it's not even
applicable.  This also helps to remove a bunch of duplicate code where these
templates are being used.
2019-07-30 14:35:05 -04:00
Mike Gerwitz 3e13b733c4 core/vector/cmatch.xml (_classify-vector_): New template
This is the analog of _classify-scalar_.
2019-07-29 14:51:38 -04:00
Mike Gerwitz 3d07597f7c core/insurance (_credit_, _debit_)[@allow-zero@]: Add 2019-03-19 13:19:33 -04:00
Mike Gerwitz 26249f8dbb core: Add _vfilter-mask_
* core/test/core/suite.xml: Import `vector/filter'.
* core/test/core/vector/filter.xml: New package.
* core/vector/filter.xml (_vfilter-mask_, _vfilter_mask): New template, function.
2019-02-14 15:05:49 -05:00
Mike Gerwitz 279245d168 core: Add missing _minreduce_ @isvector@ test
core/test/core/vector/minmax.xml: Add missing @isvector@ test.
2019-02-13 16:10:26 -05:00
Mike Gerwitz 46b7c234dd core: vector/minmax/_minreduce_: New template
* test/core/suite.xml: Import `test/core/vector/minmax'.
* test/core/vector/minmax.xml: New package.
* vector/minmax.xml (_minreduce_, _minreduce): New template, function.
2019-02-13 14:38:08 -05:00
Mike Gerwitz e022a3133d Copyright year simplification and update to Ryan Specialty Group
This now uses year ranges, which I'll update annually.

This also renames "R-T Specialty" to "Ryan Specialty Group".  The latter is
the parent company of the former.  I was originally employed under the
former when LoVullo Associates was purchased, by I now work for the parent
company.
2019-02-07 13:23:09 -05:00
Mike Gerwitz 11109d4361 core: Add _where-*_ query predicate templates
These provide a more pleasent abstraction than having to reference CMP_OP_*
constants.

* core/test/core/vector/interpolate.xml: {t:when=>t:where-eq}.
* core/test/core/vector/table.xml: Likewise, but using the other variants
    where appropriate given the value of `@op'.
* core/vector/interpolate.xml: Likewise.
* core/vector/table.xml (_when_, _where_): Rename former to latter and
    provide deprecation warning.
  (_when-lt_, _when-lte_, _when-gt_, _when-gte_): Add abstractions.
* src/current/rater.xsd: Permit template variable as tenplate name.
2019-02-04 10:22:46 -05:00
Mike Gerwitz 36a3e348b6 core: Add comparison operators for table query predicates
This is fairly primitive support and it completely sidesteps the bisect
algorithm for now.  The next commit will abstract this a little bit further
to make it less awkward to use.

* core/test/core/vector/table.xml: New test cases.
* core/vector/filter.xml (CmpOp): New typedef.
  (mfilter): Document that bisecting will not happen unless `CMP_OP_EQ'
    is used.  Implement that restriction.
    [op]: New parameter.  Provide it to `mrange'.
  (_mfilter, _mrange_cmp): Rename from `_mfilter'.  Implement new comparison
    check based on `op'
    [op]: New argument.
* core/vector/table.xml (_when_)[@op@]: New param.  Add it to the produced
    vector.
  (_mquery): Unpack op (from `_when_') in call to `mfilter'.
2019-02-04 10:22:46 -05:00
Mike Gerwitz 74f8b56fcc Use some modern shorthands for core/vector/{table,filter}
Just trying to clean up a little as I go to start to make it easier
to understand.

* core/vector/filter.xml: Use _when-*_ templates and c:recurse.
* core/vector/table.xml: Likewise.
2019-02-04 10:22:46 -05:00
Mike Gerwitz c68c2f41d5 core/vector/table: Add specification for main templates
* core/test/core/suite.xml: Import core/test/core/vector/table.
* core/test/core/vector/table.xml: New specification.
2019-02-04 10:22:46 -05:00
Mike Gerwitz 73d691273e core: Replace all occurrences of c:{set=>vector}
The former is deprecated and never made any sense at all.
2019-02-01 16:01:56 -05:00
Mike Gerwitz e4ccf3e90a test/spec: Work around expand-sequence bug
* core/test/spec.xml (_describe_): Enclose aggregate classification in a
  series of nested expand-sequence to work around bug (described in
  comment), which was causing test cases to not be compiled.
2019-02-01 16:01:56 -05:00
Mike Gerwitz a7f186beff [BC BREAK] rater/core/insurance (_premium_): Add zero and negative assertions
This is a BC break since this generates assertions by default.  To maintain
BC, set `@allow-zero@' and `@allow-negative@' to `true' in existing template
applications.

* core/insurance.xml
  (assert_ignore_premium_zero, assert_ignore_premium_negative): New params.
  (_premium_): Generate assertions.
    [@allow-zero@, @allow-negative@]: New params.
2019-01-02 16:58:56 -05:00
Mike Gerwitz dec3f2ef35 rater/core/insurance (_factor_): gt{=>e} for negative assertions 2019-01-02 16:56:57 -05:00
Mike Gerwitz 98494edee5 core build
This is the start of a working build for core.

* .gitignore: Ignore generated files from configuration and build.
* build.xml: Copy from rater repo.  This is the last remaining ant-based
    dependency and can be gotten rid of; see comments.
* configure.ac: New file.
* rater/build-aux, rater/src: New symlinks.
2018-11-08 11:15:12 -05:00
Mike Gerwitz 970c3531c5 core/COPYING: Remove duplicate
This is no longer necessary since tame-core was merged
with this repo.
2018-11-07 23:27:18 -05:00