Skip to content

Commit

Permalink
Merge pull request #2314 from rakudo/normalized-ZDRs
Browse files Browse the repository at this point in the history
Normalize ZDRs
  • Loading branch information
zoffixznet committed Sep 24, 2018
2 parents d3ba449 + 97e8413 commit 75cf8be
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 22 deletions.
51 changes: 32 additions & 19 deletions src/core/Rat.pm6
Expand Up @@ -38,15 +38,25 @@ my class FatRat is Cool does Rational[Int, Int] {

sub DIVIDE_NUMBERS(Int:D \nu, Int:D \de, \t1, \t2) {
nqp::stmts(
(my $gcd := de ?? nqp::gcd_I(nqp::decont(nu), nqp::decont(de), Int) !! 1),
(my $numerator := nqp::div_I(nqp::decont(nu), $gcd, Int)),
(my $denominator := nqp::div_I(nqp::decont(de), $gcd, Int)),
(my $numerator),
(my $denominator),
nqp::if(
nqp::islt_I($denominator, 0),
de,
nqp::stmts(
($numerator := nqp::neg_I($numerator, Int)),
($denominator := nqp::neg_I($denominator, Int)))),
RAKUDO_INTERNAL_DIVIDE_NUMBERS_NO_NORMALIZE $numerator, $denominator, t1, t2)
(my $gcd := nqp::gcd_I(nqp::decont(nu), nqp::decont(de), Int)),
($numerator := nqp::div_I(nqp::decont(nu), $gcd, Int)),
($denominator := nqp::div_I(nqp::decont(de), $gcd, Int)),
nqp::if(
nqp::islt_I($denominator, 0),
nqp::stmts(
($numerator := nqp::neg_I($numerator, Int)),
($denominator := nqp::neg_I($denominator, Int))))),
nqp::stmts(
($numerator := nqp::box_i(
nqp::isgt_I(nqp::decont(nu), 0) ?? 1 !! nu ?? -1 !! 0, Int)),
($denominator := nqp::decont(de)))),
RAKUDO_INTERNAL_DIVIDE_NUMBERS_NO_NORMALIZE
$numerator, $denominator, t1, t2)
}

sub RAKUDO_INTERNAL_DIVIDE_NUMBERS_NO_NORMALIZE(\nu, \de, \t1, \t2) {
Expand Down Expand Up @@ -206,24 +216,27 @@ multi sub infix:<**>(Rational:D \a, Int:D \b) {
}

multi sub infix:<==>(Rational:D \a, Rational:D \b) {
nqp::isfalse(a.denominator) || nqp::isfalse(b.denominator)
?? a.Num == b.Num
!! a.numerator * b.denominator == b.numerator * a.denominator
nqp::hllbool(
nqp::isfalse(a.denominator) || nqp::isfalse(b.denominator)
?? nqp::iseq_I(a.numerator, b.numerator)
&& nqp::istrue(a.numerator) # NaN != NaN
!! nqp::iseq_I(
nqp::mul_I(a.numerator, b.denominator, Int),
nqp::mul_I(b.numerator, a.denominator, Int)))
}
multi sub infix:<==>(Rational:D \a, Int:D \b) {
a.numerator == b && a.denominator == 1
nqp::hllbool(
nqp::iseq_I(a.numerator, nqp::decont(b)) && nqp::iseq_I(a.denominator, 1))
}
multi sub infix:<==>(Int:D \a, Rational:D \b) {
a == b.numerator && b.denominator == 1;
nqp::hllbool(
nqp::iseq_I(nqp::decont(a), b.numerator) && nqp::iseq_I(b.denominator, 1))
}
multi sub infix:<===>(Rational:D \a, Rational:D \b --> Bool:D) {
# Check whether we have 0-denominator rationals as well. Those can
# be `==` but have different numerator values and so should not `===` True.
# Since we're already checking equality first, we only need to check the
# zeroeness of the denominator of just one parameter
a.WHAT =:= b.WHAT
&& (a == b || (a.isNaN && b.isNaN))
&& (a.denominator.Bool || a.numerator == b.numerator)
nqp::hllbool(
nqp::eqaddr(a.WHAT, b.WHAT)
&& nqp::iseq_I(a.numerator, b.numerator)
&& nqp::iseq_I(a.denominator, b.denominator))
}

multi sub infix<»(Rational:D \a, Rational:D \b) {
Expand Down
14 changes: 11 additions & 3 deletions src/core/Rational.pm6
Expand Up @@ -24,11 +24,12 @@ my role Rational[::NuT = Int, ::DeT = ::("NuT")] does Real {
method new(NuT \nu = 0, DeT \de = 1) {
nqp::unless(
de,
nqp::p6bindattrinvres( # zero-denominator-rational; bind-as-is
nqp::p6bindattrinvres( # zero-denominator-rational; normalize
nqp::p6bindattrinvres(
nqp::create(self),
::?CLASS, '$!denominator', nqp::decont(de)),
::?CLASS, '$!numerator', nqp::decont(nu)),
::?CLASS, '$!numerator', nqp::box_i(
nqp::isgt_I(nqp::decont(nu), 0) ?? 1 !! nu ?? -1 !! 0, nu.WHAT)),
nqp::stmts( # normal rational
(my $gcd := nqp::gcd_I(nqp::decont(nu), nqp::decont(de), Int)),
(my $nu := nqp::div_I(nqp::decont(nu), $gcd, NuT)),
Expand All @@ -54,12 +55,16 @@ my role Rational[::NuT = Int, ::DeT = ::("NuT")] does Real {
}

method floor(Rational:D:) {
$!denominator || fail X::Numeric::DivideByZero.new:
:details('when calling .floor on Rational');
$!denominator == 1
?? $!numerator
!! $!numerator div $!denominator
}

method ceiling(Rational:D:) {
$!denominator || fail X::Numeric::DivideByZero.new:
:details('when calling .ceiling on Rational');
$!denominator == 1
?? $!numerator
!! ($!numerator div $!denominator + 1)
Expand Down Expand Up @@ -87,6 +92,9 @@ my role Rational[::NuT = Int, ::DeT = ::("NuT")] does Real {
}

multi method Str(::?CLASS:D:) {
$!denominator || die X::Numeric::DivideByZero.new:
:details('when coercing Rational to Str');

my $whole = self.abs.floor;
my $fract = self.abs - $whole;

Expand Down Expand Up @@ -119,7 +127,7 @@ my role Rational[::NuT = Int, ::DeT = ::("NuT")] does Real {
# TODO v6.d FatRats are tested in roast to have a minimum
# precision pf 6 decimal places - mostly due to there being no
# formal spec and the desire to test SOMETHING. With this
# speed increase, 16 digits would work fine; but it isn't spec.
# speed increase, 16 digits would work fine; but it isn't spec.
#if $!denominator < 1000000000000000 {
# $precision = 16;
# $fract *= 10000000000000000;
Expand Down

0 comments on commit 75cf8be

Please sign in to comment.