We have seen the mysterious `DIVIDE_NUMBERS` function a couple of times already. Let us keep our focus on it today.

The function is a part of the Rat data type. It lives in src/core/Rat.pm together with its sister, `DON'T_DIVIDE_NUMBERS`. (An apostrophe is a valid character for identifiers in Perl 6; it is not a namespace separator as in Perl 4 or Perl 5.)

First, let us read the functions, starting with the simpler one.

subDON'T_DIVIDE_NUMBERS(Int:D \nu, Int:D \de, $t1, $t2) { nqp::istype($t1, FatRat) || nqp::istype($t2, FatRat) ?? nqp::p6bindattrinvres( nqp::p6bindattrinvres( nqp::create(FatRat), FatRat, '$!numerator', nqp::decont(nu)), FatRat, '$!denominator', nqp::decont(de)) !! nqp::p6bindattrinvres( nqp::p6bindattrinvres( nqp::create(Rat), Rat, '$!numerator', nqp::decont(nu)), Rat, '$!denominator', nqp::decont(de)) }

The first two arguments, `nu` and `de`, are the numerator and the denominator of the future Rat number. The other two arguments carry their types (we’ll see how they are used in a bit).

So, what does the function do? If creates either a FatRat or a Rat number. You get a FatRat value if at least one of the arguments is a FatRat number. In the opposite case, a Rat value is created. In the rest, both branches are identical:

nqp::p6bindattrinvres( nqp::p6bindattrinvres( nqp::create(Rat), Rat, '$!numerator', nqp::decont(nu)), Rat, '$!denominator', nqp::decont(de))

The `nqp::create` function creates an object, whose attributes, `$!numerator` and `$!denominator`, are later filled up with the corresponding values. Refer to one of the recent posts to see how `nqp::p6bindattrinvres` was used to speed up the creation of a Rat value.

Now, to the bigger function.

subDIVIDE_NUMBERS(Int:D \nu, Int:D \de, \t1, \t2) { nqp::stmts( (my Int $gcd := de == 0 ?? 1 !! nu gcd de), (my Int $numerator := nu div $gcd), (my Int $denominator := de div $gcd), nqp::if( $denominator < 0, nqp::stmts( ($numerator := -$numerator), ($denominator := -$denominator))), nqp::if( nqp::istype(t1, FatRat) || nqp::istype(t2, FatRat), nqp::p6bindattrinvres( nqp::p6bindattrinvres(nqp::create(FatRat),FatRat,'$!numerator',$numerator), FatRat,'$!denominator',$denominator), nqp::if( $denominator < UINT64_UPPER, nqp::p6bindattrinvres( nqp::p6bindattrinvres(nqp::create(Rat),Rat,'$!numerator',$numerator), Rat,'$!denominator',$denominator), nqp::p6box_n(nqp::div_In($numerator, $denominator))))) }

The second part of it is very similar to what we already discussed, but there are some data check and conversions in the beginning.

(my Int $gcd := de == 0 ?? 1 !! nu gcd de), (my Int $numerator := nu div $gcd), (my Int $denominator := de div $gcd),

You can see here that both the numerator and the denominator are divided by their greatest common divisor. In other words, a fraction like 10/20 is converted to 1/2, and that explains the name of the function.

It also creates some kind of canonical form, making the denominator non-negative:

nqp::if( $denominator < 0, nqp::stmts( ($numerator := -$numerator), ($denominator := -$denominator))),

Great, we have two functions that either immediately create a Rat (or FatRat) value or reduce the fraction before creating a Rat (or FatRat) value.

The Rat class does the Rational role, and you can find another method that reduces the fraction there:

methodREDUCE-ME(--> Nil) { if $!denominator > 1 { my $gcd = $!denominator gcd $!numerator; if $gcd > 1 { nqp::bindattr(self, self.WHAT, '$!numerator', $!numerator div $gcd); nqp::bindattr(self, self.WHAT, '$!denominator', $!denominator div $gcd); } } }

Our next step is to see where the functions are used. All of them are in the src/core/Rat.pm file.

The simplest is the function for the `/` infix with two integer operands:

multi sub infix:</>(Int:D \a, Int:D \b) { DIVIDE_NUMBERS a, b, a, b }

Notice that the arguments are just repeated twice, while they are used differently as soon as they reach the `DIVIDE_NUMBERS` function.

With other combinations of the types of the arguments, data flow is a bit more sophisticated:

multi sub infix:</>(Int:D \a, Rational:D \b) { b.REDUCE-ME; # RT #126391: [BUG] Bad "divide by 0" error message DIVIDE_NUMBERS b.denominator * a, b.numerator, a, b; }

Please explore the src/core/Rat.pm file yourself if you want to see more examples. They all follow the idea that we already illustrated. Maybe with one exception: the code that may generate exceptions from the `**` operator.

multi sub infix:<**>(Rational:D \a, Int:D \b) { b >= 0 ?? DIVIDE_NUMBERS (a.numerator ** b //fail(a.numerator.abs > a.denominator ?? X::Numeric::Overflow !! X::Numeric::Underflow).new), a.denominator ** b, # we presume it likely already blew up on the numerator a, b !! DIVIDE_NUMBERS (a.denominator ** -b //fail(a.numerator.abs < a.denominator ?? X::Numeric::Overflow !! X::Numeric::Underflow).new), a.numerator ** -b, a, b }

And that’s it for today. Stay tuned!

## 3 thoughts on “38. To divide or not to divide”