Skip to content

Commit

Permalink
Use mpq functions for Rational{BigInt} (JuliaLang#38520)
Browse files Browse the repository at this point in the history
  • Loading branch information
simonbyrne committed Nov 30, 2020
1 parent 3ffb4fb commit 217a449
Showing 1 changed file with 104 additions and 0 deletions.
104 changes: 104 additions & 0 deletions base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -837,4 +837,108 @@ if Limb === UInt
end
end

module MPQ

# Rational{BigInt}
import .Base: unsafe_rational, __throw_rational_argerror_zero
import ..GMP: BigInt, MPZ, Limb, isneg

mutable struct _MPQ
num_alloc::Cint
num_size::Cint
num_d::Ptr{Limb}
den_alloc::Cint
den_size::Cint
den_d::Ptr{Limb}
# to prevent GC
rat::Rational{BigInt}
end

const mpq_t = Ref{_MPQ}

_MPQ(x::BigInt,y::BigInt) = _MPQ(x.alloc, x.size, x.d,
y.alloc, y.size, y.d,
unsafe_rational(BigInt, x, y))
_MPQ() = _MPQ(BigInt(), BigInt())
_MPQ(x::Rational{BigInt}) = _MPQ(x.num, x.den)

function sync_rational!(xq::_MPQ)
xq.rat.num.alloc = xq.num_alloc
xq.rat.num.size = xq.num_size
xq.rat.num.d = xq.num_d
xq.rat.den.alloc = xq.den_alloc
xq.rat.den.size = xq.den_size
xq.rat.den.d = xq.den_d
return xq.rat
end

function Rational{BigInt}(num::BigInt, den::BigInt)
if iszero(den)
iszero(num) && __throw_rational_argerror_zero(BigInt)
num = isneg(num) ? -one(BigInt) : one(BigInt)
return unsafe_rational(BigInt, num, den)
end
xq = _MPQ(MPZ.set(num), MPZ.set(den))
ccall((:__gmpq_canonicalize, :libgmp), Cvoid, (mpq_t,), xq)
return sync_rational!(xq)
end

function Base.:+(x::Rational{BigInt}, y::Rational{BigInt})
if iszero(x.den) || iszero(y.den)
if iszero(x.den) && iszero(y.den) && isneg(x.num) != isneg(y.num)
throw(DivideError())
end
return iszero(x.den) ? x : y
end
zq = _MPQ()
ccall((:__gmpq_add, :libgmp), Cvoid,
(mpq_t,mpq_t,mpq_t), zq, _MPQ(x), _MPQ(y))
return sync_rational!(zq)
end
function Base.:-(x::Rational{BigInt}, y::Rational{BigInt})
if iszero(x.den) || iszero(y.den)
if iszero(x.den) && iszero(y.den) && isneg(x.num) == isneg(y.num)
throw(DivideError())
end
return iszero(x.den) ? x : -y
end
zq = _MPQ()
ccall((:__gmpq_sub, :libgmp), Cvoid,
(mpq_t,mpq_t,mpq_t), zq, _MPQ(x), _MPQ(y))
return sync_rational!(zq)
end
function Base.:*(x::Rational{BigInt}, y::Rational{BigInt})
if iszero(x.den) || iszero(y.den)
if iszero(x.num) || iszero(y.num)
throw(DivideError())
end
return xor(isneg(x.num),isneg(y.num)) ? -one(BigInt)//zero(BigInt) : one(BigInt)//zero(BigInt)
end
zq = _MPQ()
ccall((:__gmpq_mul, :libgmp), Cvoid,
(mpq_t,mpq_t,mpq_t), zq, _MPQ(x), _MPQ(y))
return sync_rational!(zq)
end
function Base.://(x::Rational{BigInt}, y::Rational{BigInt})
if iszero(x.den)
if iszero(y.den)
throw(DivideError())
end
return isneg(y.num) ? -x : x
elseif iszero(y.den)
return y.den // y.num
elseif iszero(y.num)
if iszero(x.num)
throw(DivideError())
end
return (isneg(x.num) ? -one(BigFloat) : one(BigFloat)) // y.num
end
zq = _MPQ()
ccall((:__gmpq_div, :libgmp), Cvoid,
(mpq_t,mpq_t,mpq_t), zq, _MPQ(x), _MPQ(y))
return sync_rational!(zq)
end

end # MPQ module

end # module

0 comments on commit 217a449

Please sign in to comment.