Last active
April 16, 2016 06:54
-
-
Save will/3b5e2d8e33d90436a2174a5c3b1e9dd9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# https://github.com/wbhart/mpir/blob/master/gmp-h.in#L262-L266 | |
# https://gmplib.org/manual/Rational-Arithmetic.html#Rational-Arithmetic | |
require "spec" | |
require "big_int" | |
lib LibGMP | |
struct MPQ | |
_mp_num : MPZ | |
_mp_den : MPZ | |
end | |
fun mpq_init = __gmpq_init(x : MPQ*) | |
fun mpq_get_str = __gmpq_get_str(str : UInt8*, base : Int, op : MPQ*) : UInt8* | |
fun mpq_set_num = __gmpq_set_num(x : MPQ*, num : MPZ*) | |
fun mpq_set_den = __gmpq_set_den(x : MPQ*, den : MPZ*) | |
fun mpq_get_num = __gmpq_get_num(rop : MPZ*, op : MPQ*) | |
fun mpq_get_den = __gmpq_get_den(rop : MPZ*, op : MPQ*) | |
fun mpq_denref = __gmpq_numref(op : MPQ*) : MPZ* | |
fun mpq_canonicalize = __gmpq_canonicalize(x : MPQ*) | |
fun mpq_get_d = __gmpq_get_d(x : MPQ*) : Float64 | |
# compare | |
fun mpq_cmp = __gmpq_cmp(x : MPQ*, o : MPQ*) : Int32 | |
# math | |
fun mpq_add = __gmpq_add(rop : MPQ*, op1 : MPQ*, op2 : MPQ*) | |
fun mpq_sub = __gmpq_sub(rop : MPQ*, op1 : MPQ*, op2 : MPQ*) | |
fun mpq_mul = __gmpq_mul(rop : MPQ*, op1 : MPQ*, op2 : MPQ*) | |
fun mpq_div = __gmpq_div(rop : MPQ*, op1 : MPQ*, op2 : MPQ*) | |
fun mpq_inv = __gmpq_inv(rop : MPQ*, op1 : MPQ*) | |
fun mpq_neg = __gmpq_neg(rop : MPQ*, op1 : MPQ*) | |
fun mpq_abs = __gmpq_abs(rop : MPQ*, op1 : MPQ*) | |
fun mpq_div_2exp = __gmpq_div_2exp(q : MPQ*, n : MPQ*, b : BitcntT) | |
fun mpq_mul_2exp = __gmpq_mul_2exp(rop : MPQ*, op1 : MPQ*, op2 : BitcntT) | |
end | |
class BigRational | |
include Comparable(BigRational) | |
include Comparable(Int) | |
def initialize(numerator : Int, denominator : Int) | |
check_division_by_zero denominator | |
initialize BigInt.new(numerator), BigInt.new(denominator) | |
end | |
def initialize(numerator : BigInt, denominator : BigInt) | |
check_division_by_zero denominator | |
LibGMP.mpq_init(out @mpq) | |
LibGMP.mpq_set_num(mpq, numerator.to_unsafe) | |
LibGMP.mpq_set_den(mpq, denominator.to_unsafe) | |
LibGMP.mpq_canonicalize(mpq) | |
end | |
# :nodoc: | |
def initialize(@mpq : LibGMP::MPQ) | |
end | |
# :nodoc: | |
def self.new | |
LibGMP.mpq_init(out mpq) | |
yield pointerof(mpq) | |
new(mpq) | |
end | |
def numerator | |
BigInt.new { |mpz| LibGMP.mpq_get_num(mpz, self) } | |
end | |
def denominator | |
BigInt.new { |mpz| LibGMP.mpq_get_den(mpz, self) } | |
end | |
def <=>(other : BigRational) | |
LibGMP.mpq_cmp(mpq, other) | |
end | |
def <=>(other : BigRational::Coercible) | |
LibGMP.mpq_cmp(mpq, other.to_big_r) | |
end | |
def +(other : BigRational) | |
BigRational.new { |mpq| LibGMP.mpq_add(mpq, self, other) } | |
end | |
def +(other : BigRational::Coercible) | |
self + other.to_big_r | |
end | |
def -(other : BigRational) | |
BigRational.new { |mpq| LibGMP.mpq_sub(mpq, self, other) } | |
end | |
def -(other : BigRational::Coercible) | |
self - other.to_big_r | |
end | |
def *(other : BigRational) | |
BigRational.new { |mpq| LibGMP.mpq_mul(mpq, self, other) } | |
end | |
def *(other : BigRational::Coercible) | |
self * other.to_big_r | |
end | |
def /(other : BigRational) | |
check_division_by_zero other | |
BigRational.new { |mpq| LibGMP.mpq_div(mpq, self, other) } | |
end | |
def /(other : BigRational::Coercible) | |
self / other.to_big_r | |
end | |
def >>(other : Int) | |
BigRational.new { |mpq| LibGMP.mpq_div_2exp(mpq, self, other) } | |
end | |
def <<(other : Int) | |
BigRational.new { |mpq| LibGMP.mpq_mul_2exp(mpq, self, other) } | |
end | |
def - | |
BigRational.new { |mpq| LibGMP.mpq_neg(mpq, self) } | |
end | |
def inv | |
check_division_by_zero self | |
BigRational.new { |mpq| LibGMP.mpq_inv(mpq, self) } | |
end | |
def abs | |
BigRational.new { |mpq| LibGMP.mpq_abs(mpq, self) } | |
end | |
def to_f | |
to_f64 | |
end | |
def to_f32 | |
to_f64.to_f32 | |
end | |
def to_f64 | |
LibGMP.mpq_get_d(mpq) | |
end | |
def to_s | |
String.new(to_cstr) | |
end | |
def to_s(io) | |
str = to_cstr | |
io.write_utf8 Slice.new(str, LibC.strlen(str)) | |
end | |
def inspect | |
to_s | |
end | |
def inspect(io) | |
to_s io | |
end | |
private def mpq | |
pointerof(@mpq) | |
end | |
def to_unsafe | |
mpq | |
end | |
private def to_cstr | |
LibGMP.mpq_get_str(nil, 10, mpq) | |
end | |
private def check_division_by_zero(value) | |
# TODO why does swapping value == 0 fail | |
raise DivisionByZero.new if 0 == value | |
end | |
module Coercible | |
include Comparable(BigRational) | |
abstract def to_big_r : BigRational | |
def <=>(other : BigRational) | |
-(other <=> self) | |
end | |
def +(other : BigRational) | |
other + self | |
end | |
def -(other : BigRational) | |
self.to_big_r - other | |
end | |
def /(other : BigRational) | |
self.to_big_r / other | |
end | |
def *(other : BigRational) | |
other * self | |
end | |
end | |
end | |
struct Int | |
include BigRational::Coercible | |
def to_big_r | |
BigRational.new(self, 1) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "./bigq" | |
def b(n, d) | |
BigRational.new(n, d) | |
end | |
describe BigRational do | |
it "initialize" do | |
BigRational.new(BigInt.new(10), BigInt.new(3)) | |
.should eq(BigRational.new(10, 3)) | |
expect_raises(DivisionByZero) do | |
BigRational.new(BigInt.new(2), BigInt.new(0)) | |
end | |
expect_raises(DivisionByZero) do | |
BigRational.new(2, 0) | |
end | |
end | |
it "#numerator" do | |
b(10, 3).numerator.should eq(BigInt.new(10)) | |
end | |
it "#denominator" do | |
b(10, 3).denominator.should eq(BigInt.new(3)) | |
end | |
it "#to_s" do | |
BigRational.new(10, 3).to_s.should eq("10/3") | |
BigRational.new(90, 3).to_s.should eq("30") | |
BigRational.new(1, 98).to_s.should eq("1/98") | |
end | |
it "#to_f64" do | |
r = BigRational.new(10, 3) | |
f = 10.to_f64 / 3.to_f64 | |
r.to_f64.should be_close(f, 0.001) | |
end | |
it "#to_f" do | |
r = BigRational.new(10, 3) | |
f = 10.to_f64 / 3.to_f64 | |
r.to_f.should be_close(f, 0.001) | |
end | |
it "#to_f" do | |
r = BigRational.new(10, 3) | |
f = 10.to_f64 / 3.to_f64 | |
r.to_f32.should be_close(f, 0.001) | |
end | |
it "Int#to_big_r" do | |
3.to_big_r.should eq(b(3, 1)) | |
end | |
it "#<=>(:BigRational) and Compareable" do | |
a = BigRational.new(11, 3) | |
b = BigRational.new(12, 3) | |
e = a | |
l = BigRational.new(10, 3) | |
# sainty check things aren't swapped | |
[b, e, l].each { |o| (a <=> o).should eq(a.to_f <=> o.to_f) } | |
(a < b).should eq(true) | |
(a <=> b).should eq(-1) | |
(a == e).should eq(true) | |
(a <=> e).should eq(0) | |
(a > l).should eq(true) | |
(a <=> l).should eq(1) | |
end | |
it "#<=>(:Int) and Compareable" do | |
a = BigRational.new(10, 2) | |
(a < 6).should eq(true) | |
(a <=> 6).should eq(-1) | |
# (a == 5).should eq(true) #fails | |
(5 == a).should eq(true) | |
(a <=> 5).should eq(0) | |
(a > 4).should eq(true) | |
(a <=> 4).should eq(1) | |
end | |
it "#+" do | |
(b(10, 7) + b(3, 7)).should eq(b(13, 7)) | |
(0 + b(10, 7) + 3).should eq(b(31, 7)) | |
end | |
it "#-" do | |
(b(10, 7) - b(3, 7)).should eq(b(7, 7)) | |
(b(10, 7) - 3).should eq(b(-11, 7)) | |
(0 - b(10, 7)).should eq(b(-10, 7)) | |
end | |
it "#*" do | |
(b(10, 7) * b(3, 7)).should eq(b(30, 49)) | |
(1 * b(10, 7) * 3).should eq(b(30, 7)) | |
end | |
it "#/" do | |
(b(10, 7) / b(3, 7)).should eq(b(10, 3)) | |
expect_raises(DivisionByZero) { b(10, 7) / b(0, 10) } | |
(b(10, 7) / 3).should eq(b(10, 21)) | |
(1 / b(10, 7)).should eq(b(7, 10)) | |
end | |
it "#- (negation)" do | |
(-b(10, 3)).should eq(b(-10, 3)) | |
end | |
it "#inv" do | |
(b(10, 3).inv).should eq(b(3, 10)) | |
expect_raises(DivisionByZero) { b(0, 3).inv } | |
end | |
it "#abs" do | |
(b(-10, 3).abs).should eq(b(10, 3)) | |
end | |
it "#<<" do | |
(b(10, 3) << 2).should eq(b(40, 3)) | |
end | |
it "#>>" do | |
(b(10, 3) >> 2).should eq(b(5, 6)) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment