# XXX: should be Rational[Int, uint] my class Rat is Cool does Rational[Int, Int] { method Rat(Rat:D: Real $? --> Rat:D) { self } method FatRat(Rat:D: Real $? --> FatRat:D) { nqp::p6bindattrinvres( nqp::p6bindattrinvres( nqp::create(FatRat),FatRat,'$!numerator',$!numerator ), FatRat,'$!denominator',$!denominator ) } multi method raku(Rat:D: --> Str:D) { if $!denominator == 1 { $!numerator ~ '.0' } else { my $d = $!denominator; unless $d == 0 { $d = $d div 5 while $d %% 5; $d = $d div 2 while $d %% 2; } if $d == 1 and (my $b := self.base(10,*)).Numeric === self { $b; } else { '<' ~ $!numerator ~ '/' ~ $!denominator ~ '>' } } } } my constant UINT64_UPPER = nqp::pow_I(2, 64, Num, Int); my class FatRat is Cool does Rational[Int, Int] { method FatRat(FatRat:D:) { self } method Rat(FatRat:D:) { $!denominator < UINT64_UPPER ?? nqp::p6bindattrinvres( nqp::p6bindattrinvres( nqp::create(Rat),Rat,'$!numerator',$!numerator ), Rat,'$!denominator',$!denominator ) !! "Cannot convert from FatRat to Rat because denominator is too big".Failure } multi method raku(FatRat:D: --> Str:D) { (nqp::eqaddr(self.WHAT,FatRat) ?? 'FatRat' !! self.^name) ~ ".new($!numerator, $!denominator)" } method UPGRADE-RAT(\nu, \de) is raw { nqp::p6bindattrinvres( nqp::p6bindattrinvres(nqp::create(FatRat),FatRat,'$!numerator',nu), FatRat,'$!denominator',de ) } } # NORMALIZE two integer values and create a Rat/FatRat/float from them. # Provide two types: if either of them is a FatRat, then a FatRat will be # returned. If a Rat is to be created, then a check for denominator overflow # is done: if that is the case, then a float will be returned. sub DIVIDE_NUMBERS( Int:D $nu, Int:D $de, \t1, \t2 ) is raw is implementation-detail { nqp::if( $de, nqp::stmts( (my \gcd := nqp::gcd_I($nu,$de,Int)), (my \numerator := nqp::div_I($nu,gcd,Int)), (my \denominator := nqp::div_I($de,gcd,Int)), nqp::if( nqp::islt_I(denominator,0), CREATE_RATIONAL_FROM_INTS( nqp::neg_I(numerator,Int), nqp::neg_I(denominator,Int), t1, t2 ), CREATE_RATIONAL_FROM_INTS( numerator, denominator, t1, t2 ) ) ), CREATE_RATIONAL_FROM_INTS( nqp::box_i(nqp::isgt_I($nu,0) || nqp::neg_i(nqp::istrue($nu)),Int), 0, t1, t2 ) ) } # Initialize the $*RAT-OVERFLOW dynamic var so that it can be used PROCESS::<$RAT-OVERFLOW> = Num; # ALL RATIONALS MUST BE NORMALIZED, however in some operations we cannot # ever get a non-normalized Rational, if we start with a normalized Rational. # For such cases, we can use this routine, to bypass normalization step, # which would be useless. Also used when normalization *was* needed. proto sub CREATE_RATIONAL_FROM_INTS(|) is implementation-detail {*} multi sub CREATE_RATIONAL_FROM_INTS(Int:D $nu, Int:D $de, Any, Any) is raw { nqp::islt_I($de,UINT64_UPPER) # do we need to downgrade to float? ?? nqp::p6bindattrinvres( # no, we need to keep a Rat nqp::p6bindattrinvres(nqp::create(Rat),Rat,'$!numerator',$nu), Rat,'$!denominator',$de ) !! $*RAT-OVERFLOW.UPGRADE-RAT($nu, $de) } # already a FatRat, so keep that multi sub CREATE_RATIONAL_FROM_INTS( Int:D $nu, Int:D $de, FatRat, Any --> FatRat:D) is raw { nqp::p6bindattrinvres( nqp::p6bindattrinvres(nqp::create(FatRat),FatRat,'$!numerator',$nu), FatRat,'$!denominator',$de ) } multi sub CREATE_RATIONAL_FROM_INTS( Int:D $nu, Int:D $de, Any, FatRat --> FatRat:D) is raw { nqp::p6bindattrinvres( nqp::p6bindattrinvres(nqp::create(FatRat),FatRat,'$!numerator',$nu), FatRat,'$!denominator',$de ) } multi sub CREATE_RATIONAL_FROM_INTS( Int:D $nu, Int:D $de, FatRat, FatRat --> FatRat:D) is raw { nqp::p6bindattrinvres( nqp::p6bindattrinvres(nqp::create(FatRat),FatRat,'$!numerator',$nu), FatRat,'$!denominator',$de ) } multi sub prefix:<->(Rat:D $a --> Rat:D) { # Rat.new(-a.numerator, a.denominator); nqp::p6bindattrinvres( nqp::clone($a), Rat,'$!numerator',nqp::neg_I($a.numerator,Int) ) } multi sub prefix:<->(FatRat:D $a --> FatRat:D) { # FatRat.new(-a.numerator, a.denominator); nqp::p6bindattrinvres( nqp::clone($a), FatRat,'$!numerator',nqp::neg_I($a.numerator,Int) ) } multi sub infix:<+>(Rational:D $a, Rational:D $b) { my \adenom := $a.denominator; my \bdenom := $b.denominator; DIVIDE_NUMBERS( $a.numerator * bdenom + $b.numerator * adenom, adenom * bdenom, $a, $b ) } multi sub infix:<+>(Rational:D $a, Int:D $b) { my \adenom := $a.denominator; DIVIDE_NUMBERS( $a.numerator + $b * adenom, adenom, $a, $b ) } multi sub infix:<+>(Int:D $a, Rational:D $b) { my \bdenom := $b.denominator; DIVIDE_NUMBERS( $a * bdenom + $b.numerator, bdenom, $a, $b ) } multi sub infix:<->(Rational:D $a, Rational:D $b) { my \adenom := $a.denominator; my \bdenom := $b.denominator; DIVIDE_NUMBERS( $a.numerator * bdenom - $b.numerator * adenom, adenom * bdenom, $a, $b ) } multi sub infix:<->(Rational:D $a, Int:D $b) { my \adenom := $a.denominator; DIVIDE_NUMBERS( $a.numerator - $b * adenom, adenom, $a, $b ) } multi sub infix:<->(Int:D $a, Rational:D $b) { my \bdenom := $b.denominator; DIVIDE_NUMBERS( $a * bdenom - $b.numerator, bdenom, $a, $b ) } multi sub infix:<*>(Rational:D $a, Rational:D $b) { DIVIDE_NUMBERS( $a.numerator * $b.numerator, $a.denominator * $b.denominator, $a, $b ) } multi sub infix:<*>(Rational:D $a, Int:D $b) { DIVIDE_NUMBERS( $a.numerator * $b, $a.denominator, $a, $b ) } multi sub infix:<*>(Int:D $a, Rational:D $b) { DIVIDE_NUMBERS( $a * $b.numerator, $b.denominator, $a, $b ) } multi sub infix:(Rational:D $a, Rational:D $b) { DIVIDE_NUMBERS( $a.numerator * $b.denominator, $a.denominator * $b.numerator, $a, $b ) } multi sub infix:(Rational:D $a, Int:D $b) { DIVIDE_NUMBERS( $a.numerator, $a.denominator * $b, $a, $b ) } multi sub infix:(Int:D $a, Rational:D $b) { DIVIDE_NUMBERS( $b.denominator * $a, $b.numerator, $a, $b ) } multi sub infix:(Int:D $a, Int:D $b) { DIVIDE_NUMBERS($a, $b, $a, $b) } multi sub infix:<%>(Rational:D $a, Int:D $b) { $a - floor($a / $b) * $b } multi sub infix:<%>(Int:D $a, Rational:D $b) { $a - floor($a / $b) * $b } multi sub infix:<%>(Rational:D $a, Rational:D $b) { $a - floor($a / $b) * $b } multi sub infix:<**>(Rational:D $a, Int:D $b) { my $nu; my $de; nqp::if( nqp::isge_I($b,0), nqp::if( # if we got Inf nqp::istype( ($nu := nqp::pow_I($a.numerator,$b,Num,Int)), Num ), X::Numeric::Overflow.new.Failure, nqp::if( # if we got Inf nqp::istype( ($de := nqp::pow_I($a.denominator,$b,Num,Int)), Num ), X::Numeric::Overflow.new.Failure, CREATE_RATIONAL_FROM_INTS($nu, $de, $a, $b) ) ), nqp::if( # if we got Inf nqp::istype( ($nu := nqp::pow_I($a.numerator,nqp::neg_I($b,Int),Num,Int)), Num ), X::Numeric::Underflow.new.Failure, nqp::if( # if we got Inf nqp::istype( ($de := nqp::pow_I($a.denominator,nqp::neg_I($b,Int),Num,Int)), Num ), X::Numeric::Underflow.new.Failure, CREATE_RATIONAL_FROM_INTS($de, $nu, $a, $b) ) ) ) } multi sub infix:<==>(Rational:D $a, Rational:D $b --> Bool:D) { nqp::hllbool( nqp::iseq_I( (my \anum := $a.numerator), $b.numerator ) && nqp::iseq_I( (my \adenom := $a.denominator), $b.denominator ) && ( # num/denom both same nqp::istrue(anum) # 1/X, Inf == Inf also true || nqp::istrue(adenom) # 0/1, NaN == NaN becomes false ) ) } multi sub infix:<==>(Rational:D $a, Int:D $b --> Bool:D) { nqp::hllbool( nqp::iseq_I($a.denominator,1) && nqp::iseq_I($a.numerator,$b) ) } multi sub infix:<==>(Int:D $a, Rational:D $b --> Bool:D) { nqp::hllbool( nqp::iseq_I($b.denominator,1) && nqp::iseq_I($a,$b.numerator) ) } multi sub infix:<===>(Rational:D $a, Rational:D $b --> Bool:D) { 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 --> Bool:D) { # a.numerator * b.denominator < b.numerator * a.denominator nqp::hllbool( nqp::islt_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 --> Bool:D) { # a.numerator < b * a.denominator nqp::hllbool( nqp::islt_I($a.numerator,nqp::mul_I($b,$a.denominator,Int)) ) } multi sub infix:«<»(Int:D $a, Rational:D $b --> Bool:D) { # a * b.denominator < b.numerator nqp::hllbool( nqp::islt_I(nqp::mul_I($a,$b.denominator,Int),$b.numerator) ) } multi sub infix:«<=»(Rational:D $a, Rational:D $b --> Bool:D) { # a.numerator * b.denominator <= b.numerator * a.denominator nqp::hllbool( nqp::isle_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 --> Bool:D) { # a.numerator <= b * a.denominator nqp::hllbool( nqp::isle_I($a.numerator,nqp::mul_I($b,$a.denominator,Int)) ) } multi sub infix:«<=»(Int:D $a, Rational:D $b --> Bool:D) { # a * b.denominator <= b.numerator nqp::hllbool( nqp::isle_I(nqp::mul_I($a,$b.denominator,Int),$b.numerator) ) } multi sub infix:«>»(Rational:D $a, Rational:D $b --> Bool:D) { # a.numerator * b.denominator > b.numerator * a.denominator nqp::hllbool( nqp::isgt_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 --> Bool:D) { # a.numerator > b * a.denominator nqp::hllbool( nqp::isgt_I($a.numerator,nqp::mul_I($b,$a.denominator,Int)) ) } multi sub infix:«>»(Int:D $a, Rational:D $b --> Bool:D) { # a * b.denominator > b.numerator nqp::hllbool( nqp::isgt_I(nqp::mul_I($a,$b.denominator,Int),$b.numerator) ) } multi sub infix:«>=»(Rational:D $a, Rational:D $b --> Bool:D) { # a.numerator * b.denominator >= b.numerator * a.denominator nqp::hllbool( nqp::isge_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 --> Bool:D) { # a.numerator >= b * a.denominator nqp::hllbool( nqp::isge_I($a.numerator,nqp::mul_I($b,$a.denominator,Int)) ) } multi sub infix:«>=»(Int:D $a, Rational:D $b --> Bool:D) { # a * b.denominator >= b.numerator nqp::hllbool( nqp::isge_I(nqp::mul_I($a,$b.denominator,Int),$b.numerator) ) } multi sub infix:«<=>»(Rational:D $a, Rational:D $b) { # a.numerator * b.denominator <=> b.numerator * a.denominator ORDER( nqp::cmp_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 ORDER( nqp::cmp_I($a.numerator,nqp::mul_I($b,$a.denominator,Int)) ) } multi sub infix:«<=>»(Int:D $a, Rational:D $b) { # a * b.denominator <=> b.numerator ORDER( nqp::cmp_I(nqp::mul_I($a,$b.denominator,Int),$b.numerator) ) } # vim: expandtab shiftwidth=4