Skip to content

Commit

Permalink
speed up Rat addtion by reducing lazily
Browse files Browse the repository at this point in the history
About 20% faster when adding a bunch of things with the same denominator.
We do this by being lazy about reducing the fractions till we need them
reduced, that is, when we're about to display them in a way that reveals the
.nude bits, or compare them in a way which needs to know integer status.
  • Loading branch information
TimToady committed Sep 26, 2015
1 parent 0024b0a commit c8ec299
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 26 deletions.
77 changes: 52 additions & 25 deletions src/core/Rat.pm
@@ -1,28 +1,29 @@
# XXX: should be Rational[Int, UInt64]
my class Rat is Cool does Rational[Int, Int] {
method Rat (Rat:D: Real $?) { self }
method FatRat(Rat:D: Real $?) { FatRat.new($.numerator, $.denominator); }
method FatRat(Rat:D: Real $?) { FatRat.new($!numerator, $!denominator); }
method Range(Rat:U:) { Range.new(-Inf,Inf) }
multi method perl(Rat:D:) {
my $d = $.denominator;
return $.numerator ~ '.0' if $d == 1;
my $d = $!denominator;
return $!numerator ~ '.0' if $d == 1;
unless $d == 0 {
$d div= 5 while $d %% 5;
$d div= 2 while $d %% 2;
self.REDUCE-ME;
}
($d == 1) ?? self.base(10,*) !! '<' ~ $.numerator ~ '/' ~ $.denominator ~ '>';
($d == 1) ?? self.base(10,*) !! '<' ~ $!numerator ~ '/' ~ $!denominator ~ '>';
}
}

my class FatRat is Cool does Rational[Int, Int] {
method FatRat(FatRat:D: Real $?) { self }
method Rat (FatRat:D: Real $?) {
$.denominator < $UINT64_UPPER
?? Rat.new($.numerator, $.denominator)
$!denominator < $UINT64_UPPER
?? Rat.new($!numerator, $!denominator)
!! fail "Cannot convert from FatRat to Rat because denominator is too big";
}
multi method perl(FatRat:D:) {
"FatRat.new($.numerator, $.denominator)";
"FatRat.new($!numerator, $!denominator)";
}
}

Expand Down Expand Up @@ -53,6 +54,20 @@ sub DIVIDE_NUMBERS(Int:D \nu, Int:D \de, $t1, $t2) {
$r;
}

sub DON'T_DIVIDE_NUMBERS(Int:D \nu, Int:D \de, $t1, $t2) {
my $r;
if nqp::istype($t1, FatRat) || nqp::istype($t2, FatRat) {
$r := nqp::create(FatRat);
nqp::bindattr($r, FatRat, '$!numerator', nqp::decont(nu));
nqp::bindattr($r, FatRat, '$!denominator', nqp::decont(de));
} else {
$r := nqp::create(Rat);
nqp::bindattr($r, Rat, '$!numerator', nqp::decont(nu));
nqp::bindattr($r, Rat, '$!denominator', nqp::decont(de));
}
$r;
}

multi sub prefix:<->(Rat:D \a) {
Rat.new(-a.numerator, a.denominator);
}
Expand All @@ -61,24 +76,29 @@ multi sub prefix:<->(FatRat:D \a) {
}

multi sub infix:<+>(Rational \a, Rational \b) {
my Int $gcd := a.denominator gcd b.denominator;
DIVIDE_NUMBERS(
(a.numerator * (b.denominator div $gcd) + b.numerator * (a.denominator div $gcd)),
((a.denominator div $gcd) * b.denominator),
a,
b,
);
if a.denominator == b.denominator {
DON'T_DIVIDE_NUMBERS(a.numerator + b.numerator, a.denominator, a, b);
}
else {
my Int $gcd := a.denominator gcd b.denominator;
DIVIDE_NUMBERS(
(a.numerator * (b.denominator div $gcd) + b.numerator * (a.denominator div $gcd)),
((a.denominator div $gcd) * b.denominator),
a,
b,
);
}
}
multi sub infix:<+>(Rational \a, Int \b) {
DIVIDE_NUMBERS(
DON'T_DIVIDE_NUMBERS(
(a.numerator + b * a.denominator),
a.denominator,
a,
b,
);
}
multi sub infix:<+>(Int \a, Rational \b) {
DIVIDE_NUMBERS(
DON'T_DIVIDE_NUMBERS(
(a * b.denominator + b.numerator),
b.denominator,
a,
Expand All @@ -87,24 +107,29 @@ multi sub infix:<+>(Int \a, Rational \b) {
}

multi sub infix:<->(Rational \a, Rational \b) {
my Int $gcd = a.denominator gcd b.denominator;
DIVIDE_NUMBERS
a.numerator * (b.denominator div $gcd) - b.numerator * (a.denominator div $gcd),
(a.denominator div $gcd) * b.denominator,
a,
b;
if a.denominator == b.denominator {
DON'T_DIVIDE_NUMBERS(a.numerator - b.numerator, a.denominator, a, b);
}
else {
my Int $gcd = a.denominator gcd b.denominator;
DIVIDE_NUMBERS
a.numerator * (b.denominator div $gcd) - b.numerator * (a.denominator div $gcd),
(a.denominator div $gcd) * b.denominator,
a,
b;
}
}

multi sub infix:<->(Rational \a, Int \b) {
DIVIDE_NUMBERS
DON'T_DIVIDE_NUMBERS
a.numerator - b * a.denominator,
a.denominator,
a,
b;
}

multi sub infix:<->(Int \a, Rational \b) {
DIVIDE_NUMBERS
DON'T_DIVIDE_NUMBERS
a * b.denominator - b.numerator,
b.denominator,
a,
Expand Down Expand Up @@ -190,12 +215,14 @@ multi sub infix:<**>(Rational \a, Int \b) {
}

multi sub infix:<==>(Rational:D \a, Rational:D \b) {
a.numerator == b.numerator && a.denominator == b.denominator
a.numerator * b.denominator == b.numerator * a.denominator
}
multi sub infix:<==>(Rational:D \a, Int:D \b) {
a.REDUCE-ME;
a.numerator == b && a.denominator == 1
}
multi sub infix:<==>(Int:D \a, Rational:D \b) {
b.REDUCE-ME;
a == b.numerator && b.denominator == 1;
}
multi sub infix:<===>(Rational:D \a, Rational:D \b) returns Bool:D {
Expand Down
12 changes: 11 additions & 1 deletion src/core/Rational.pm
Expand Up @@ -29,7 +29,7 @@ my role Rational[::NuT, ::DeT] does Real {
$new;
}

method nude() { $!numerator, $!denominator }
method nude() { self.REDUCE-ME; $!numerator, $!denominator }
method Num() {
$!denominator == 0
?? ($!numerator < 0 ?? -Inf !! Inf)
Expand Down Expand Up @@ -172,6 +172,16 @@ my role Rational[::NuT, ::DeT] does Real {
?? $!numerator
!! self;
}

method REDUCE-ME() {
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);
}
}
}
}

# vim: ft=perl6 expandtab sw=4

0 comments on commit c8ec299

Please sign in to comment.