1

I need an Arbitrary java.lang.Number.

Here is what I came up with:

    @Provide
    Arbitrary<Number> numbers(){
        return
        Combinators.combine(
                Arbitraries.integers().between(0, 5),
                Arbitraries.integers(),
                Arbitraries.longs(),
                Arbitraries.bigIntegers(),
                Arbitraries.floats(),
                Arbitraries.doubles(),
                Arbitraries.bigDecimals()
        ).as((i, intA, longA, bigIntegerA, floatA, doubleA, bigDecimalA) ->
          new Number[]{intA, longA, bigIntegerA, floatA, doubleA, bigDecimalA}[i]
        );
    }

This kind of works but causes

STANDARD_ERROR
10:17:38      Aug 24, 2022 2:17:38 PM net.jqwik.engine.properties.RandomizedShrinkablesGenerator logEdgeCasesOutnumberTriesIfApplicable
10:17:38      INFO: Edge case generation exceeds number of tries. Stopped after 1000 generated cases.

Is there a better way to implement an Arbitrary that represents all the possible values of java.lang.Number?

Footnote:

For you set theorists out there:

Yes I know that I am forming a product and extracting a union, which is about the least efficient way to do this. Undoubtedly this confuses the shrinker to no end. This was the best I could do. If there is a way to do this as a simple union instead, please let me know.

Gabriel
  • 1,679
  • 3
  • 16
  • 37
  • 1
    Can you shortly describe what kind of array you really want? The error message has nothing to do with the shrinker, but refers to edge case generation, which gets out of hand due to combinatorial explosion. – johanneslink Aug 24 '22 at 16:39
  • Why not try with the synonym, `random` ? Same Marry with another hat. – Traian GEICU Aug 24 '22 at 17:21

2 Answers2

1

There's a simpler solution, assuming that any concrete subtype of Number should be generated:

@Property
void test(@ForAll Number aNumber) {
    System.out.println(aNumber.getClass() + " = " + aNumber);
}

@Property
void test2(@ForAll("anyNumber") Number aNumber) {
    System.out.println(aNumber.getClass() + " = " + aNumber);
}

@Provide
Arbitrary<Number> anyNumber() {
    return Arbitraries.defaultFor(Number.class);
}

As you can see, trying to generate a Number just works. Under the hood, jqwik checks for all registered arbitrary providers that are compatible with type Number and randomly chooses between them.

johanneslink
  • 4,877
  • 1
  • 20
  • 37
0

Use Arbitraries.oneOf

    @Provide
    Arbitrary<Number> numbers(){
        return Arbitraries.oneOf(
          Arbitraries.integers(),
          Arbitraries.longs(),
          Arbitraries.bigIntegers(),
          Arbitraries.floats(),
          Arbitraries.doubles(),
          Arbitraries.bigDecimals()
        );
    }

This will still cause the Edge case generation exceeds number of tries warning unfortunately.

Gabriel
  • 1,679
  • 3
  • 16
  • 37
  • 1
    The warning depends on the number of tries and how you use the arbitrary. You can also switch off the generation of edge cases completely. – johanneslink Aug 25 '22 at 06:45