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.