Overhauling the trig tests turned out to be much more epic than I had intended. Guess that’s one of the hazards of writing the test generating code in Perl 6 while Rakudo is still under heavy development.

The old test code was very, very thorough in testing each configuration. Basically, we looped through a list of angles, and tested each angle with every possible way it could be called for each type we wanted to test. So say the angle is 45 degrees. Then for the Num type, we’d call `45.Num.sin(Degrees)`

, `sin(45.Num, Degrees)`

, and `sin(:x(45.Num), Degrees)`

, and then we’d call the same for the equivalent of 45 in Radians, Gradians, and Circles. Then we’d start it all over again using Rat instead of Num. And so on and so forth.

In practice, any implementation is likely to redispatch the trig functions to two basic sets of methods, one for reals and one for complex. So that’s exactly what the tests do now (leaving out the complex case because it is long:

for TrigTest::cosines() -> $angle { my $desired-result = $angle.result; # Num.cos tests -- very thorough is_approx($angle.num(Radians).cos, $desired-result, "Num.cos - {$angle.num(Radians)} default"); for TrigTest::official_bases() -> $base { is_approx($angle.num($base).cos($base), $desired-result, "Num.cos - {$angle.num($base)} $base"); } }

Then, instead of trying to exhaustively test the rest of the cases, we try to generate one test for each case, and leave it at that. For instance, here’s the Rat tests for cos:

# Rat tests is_approx((-0.785398163404734).Rat(1e-9).cos, 0.707106781186548, "Rat.cos - -0.785398163404734"); is_approx((0).Rat(1e-9).cos(Circles), 1, "Rat.cos(Circles) - 0"); is_approx((0.785398163404734).Rat(1e-9).cos(:base(Radians)), 0.707106781186548, "Rat.cos(:base(Radians)) - 0.785398163404734"); is_approx(cos((1.57079632680947).Rat(1e-9)), 0, "cos(Rat) - 1.57079632680947"); is_approx(cos((135).Rat(1e-9), Degrees), -0.707106781186548, "cos(Rat, Degrees) - 135"); is_approx(cos(:x((3.14159265361894).Rat(1e-9))), -1, "cos(:x(Rat)) - 3.14159265361894"); is_approx(cos(:x((250).Rat(1e-9)), :base(Gradians)), -0.707106781186548, "cos(:x(Rat), :base(Gradians)) - 250");

The use of `.Rat(1e-9)`

is to ensure we have a Rat argument — longer decimal numbers may become Nums otherwise. This tests an additional case (`Rat.cos(:base(Radians))`

) that was never actually tested before. Despite that, the number of Rat tests overall goes from something like 160 to 7.

That’s good, because I added three more types to be tested. Two represent generic `Cool`

types: `Str`

and `NotComplex`

. `Str`

is used to test real numbers in a non-`Numeric`

`Cool`

type. `NotComplex`

is a non-`Numeric`

`Cool`

type which returns a `Complex`

which you call `.Numeric`

on it. The final type, `DifferentReal`

, is a `Real`

type which is not built-in.

Much to my surprise, I actually turned up a couple of bugs in Rakudo’s trig implementation when I tried this testing approach with `atan2`

, the oddball trig function which normally takes two real arguments. First, we were not being generous enough on what the second argument would accept, which was easily corrected. Second, our fake enum standing in for `TrigBase`

looks exactly like an `Int`

to Rakudo. So `2.atan2(Degrees)`

is indistinguishable from `2.atan2(1)`

, which is definitely not what the user wants. Luckily that’s a pretty silly way to call `atan2`

anyway.

The process of getting this to work turned up a few Rakudo bugs in the non-trig code, which have been reported. I won’t go into the details here.

By my reckoning, this means there is only one thing left on my list for the numeric grant, srand and rand. I will try to finish them off tomorrow. Thanks to The Perl Foundation and Ian Hague for supporting this work.

## Leave a Reply