From 5ac88f4a8b5663da5097913763ec7d117b984a1f Mon Sep 17 00:00:00 2001 From: Evan Shaw Date: Fri, 21 May 2010 16:14:55 -0700 Subject: [PATCH] big: Add Rat type Implementations are pretty rough and simple at this point, but it's a start. R=gri CC=golang-dev https://golang.org/cl/1250043 --- src/pkg/big/Makefile | 3 +- src/pkg/big/nat.go | 2 + src/pkg/big/rat.go | 270 ++++++++++++++++++++++++++++++++++++++++ src/pkg/big/rat_test.go | 185 +++++++++++++++++++++++++++ 4 files changed, 459 insertions(+), 1 deletion(-) create mode 100644 src/pkg/big/rat.go create mode 100644 src/pkg/big/rat_test.go diff --git a/src/pkg/big/Makefile b/src/pkg/big/Makefile index 520c2b852a..d858e5a687 100644 --- a/src/pkg/big/Makefile +++ b/src/pkg/big/Makefile @@ -7,8 +7,9 @@ include ../../Make.$(GOARCH) TARG=big GOFILES=\ arith.go\ - nat.go\ int.go\ + nat.go\ + rat.go\ OFILES=\ arith_$(GOARCH).$O\ diff --git a/src/pkg/big/nat.go b/src/pkg/big/nat.go index aa021e8794..19c3d88f73 100755 --- a/src/pkg/big/nat.go +++ b/src/pkg/big/nat.go @@ -10,6 +10,7 @@ // The following numeric types are supported: // // - Int signed integers +// - Rat rational numbers // // All methods on Int take the result as the receiver; if it is one // of the operands it may be overwritten (and its memory reused). @@ -39,6 +40,7 @@ type nat []Word var ( natOne = nat{1} natTwo = nat{2} + natTen = nat{10} ) diff --git a/src/pkg/big/rat.go b/src/pkg/big/rat.go new file mode 100644 index 0000000000..f35df4b461 --- /dev/null +++ b/src/pkg/big/rat.go @@ -0,0 +1,270 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements multi-precision rational numbers. + +package big + +import "strings" + +// A Rat represents a quotient a/b of arbitrary precision. The zero value for +// a Rat, 0/0, is not a legal Rat. +type Rat struct { + a Int + b nat +} + + +// NewRat creates a new Rat with numerator a and denominator b. +func NewRat(a, b int64) *Rat { + return new(Rat).SetFrac64(a, b) +} + + +// SetFrac sets z to a/b and returns z. +func (z *Rat) SetFrac(a, b *Int) *Rat { + z.a.Set(a) + z.a.neg = a.neg != b.neg + z.b = z.b.set(b.abs) + return z.norm() +} + + +// SetFrac64 sets z to a/b and returns z. +func (z *Rat) SetFrac64(a, b int64) *Rat { + z.a.SetInt64(a) + if b < 0 { + z.b.setUint64(uint64(-b)) + z.a.neg = !z.a.neg + return z.norm() + } + z.b = z.b.setUint64(uint64(b)) + return z.norm() +} + + +// SetInt sets z to x (by making a copy of x) and returns z. +func (z *Rat) SetInt(x *Int) *Rat { + z.a.Set(x) + z.b = z.b.setWord(1) + return z +} + + +// SetInt64 sets z to x and returns z. +func (z *Rat) SetInt64(x int64) *Rat { + z.a.SetInt64(x) + z.b = z.b.setWord(1) + return z +} + + +// Num returns the numerator of z; it may be <= 0. +// The result is a reference to z's numerator; it +// may change if a new value is assigned to z. +func (z *Rat) Num() *Int { + return &z.a +} + + +// Demom returns the denominator of z; it is always > 0. +// The result is a reference to z's denominator; it +// may change if a new value is assigned to z. +func (z *Rat) Denom() *Int { + return &Int{false, z.b} +} + + +func gcd(x, y nat) nat { + // Euclidean algorithm. + var a, b nat + a = a.set(x) + b = b.set(y) + for len(b) != 0 { + var q, r nat + _, r = q.div(r, a, b) + a = b + b = r + } + return a +} + + +func (z *Rat) norm() *Rat { + f := gcd(z.a.abs, z.b) + if len(z.a.abs) == 0 { + // z == 0 + z.a.neg = false // normalize sign + z.b = z.b.setWord(1) + return z + } + if f.cmp(natOne) != 0 { + z.a.abs, _ = z.a.abs.div(nil, z.a.abs, f) + z.b, _ = z.b.div(nil, z.b, f) + } + return z +} + + +func mulNat(x *Int, y nat) *Int { + var z Int + z.abs = z.abs.mul(x.abs, y) + z.neg = len(z.abs) > 0 && x.neg + return &z +} + + +// Cmp compares x and y and returns: +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +func (x *Rat) Cmp(y *Rat) (r int) { + return mulNat(&x.a, y.b).Cmp(mulNat(&y.a, x.b)) +} + + +// Add sets z to the sum x+y and returns z. +func (z *Rat) Add(x, y *Rat) *Rat { + a1 := mulNat(&x.a, y.b) + a2 := mulNat(&y.a, x.b) + z.a.Add(a1, a2) + z.b = z.b.mul(x.b, y.b) + return z.norm() +} + + +// Sub sets z to the difference x-y and returns z. +func (z *Rat) Sub(x, y *Rat) *Rat { + a1 := mulNat(&x.a, y.b) + a2 := mulNat(&y.a, x.b) + z.a.Sub(a1, a2) + z.b = z.b.mul(x.b, y.b) + return z.norm() +} + + +// Mul sets z to the product x*y and returns z. +func (z *Rat) Mul(x, y *Rat) *Rat { + z.a.Mul(&x.a, &y.a) + z.b = z.b.mul(x.b, y.b) + return z.norm() +} + + +// Quo sets z to the quotient x/y and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +func (z *Rat) Quo(x, y *Rat) *Rat { + if len(y.a.abs) == 0 { + panic("division by zero") + } + z.a.abs = z.a.abs.mul(x.a.abs, y.b) + z.b = z.b.mul(x.b, y.a.abs) + z.a.neg = x.a.neg != y.a.neg + return z.norm() +} + + +// Neg sets z to -x (by making a copy of x if necessary) and returns z. +func (z *Rat) Neg(x *Rat) *Rat { + z.a.Neg(&x.a) + z.b = z.b.set(x.b) + return z +} + + +// Set sets z to x (by making a copy of x if necessary) and returns z. +func (z *Rat) Set(x *Rat) *Rat { + z.a.Set(&x.a) + z.b = z.b.set(x.b) + return z +} + + +// SetString sets z to the value of s and returns z and a boolean indicating +// success. s can be given as a fraction "a/b" or as a decimal number "a.b". +// If the operation failed, the value of z is undefined. +func (z *Rat) SetString(s string) (*Rat, bool) { + if len(s) == 0 { + return z, false + } + + // Check for a decimal point + sep := strings.Index(s, ".") + if sep < 0 { + // Check for a quotient + sep = strings.Index(s, "/") + if sep < 0 { + // Just read in the string as an integer + if _, ok := z.a.SetString(s, 10); !ok { + return z, false + } + z.b = z.b.setWord(1) + return z, true + } + if _, ok := z.a.SetString(s[0:sep], 10); !ok { + return z, false + } + s = s[sep+1:] + var n int + if z.b, _, n = z.b.scan(s, 10); n != len(s) { + return z, false + } + + return z.norm(), true + } + + s = s[0:sep] + s[sep+1:] + if _, ok := z.a.SetString(s, 10); !ok { + return z, false + } + z.b = z.b.expNN(natTen, nat{Word(len(s) - sep)}, nil) + + return z.norm(), true +} + + +// String returns a string representation of z in the form "a/b". +func (z *Rat) String() string { + s := z.a.String() + if len(z.b) == 1 && z.b[0] == 1 { + return s + } + return s + "/" + z.b.string(10) +} + + +// FloatString returns a string representation of z in decimal form with prec +// digits of precision after the decimal point and the last digit rounded. +func (z *Rat) FloatString(prec int) string { + q, r := nat{}.div(nat{}, z.a.abs, z.b) + + s := "" + if z.a.neg { + s = "-" + } + s += q.string(10) + + if len(z.b) == 1 && z.b[0] == 1 { + return s + } + + p := nat{}.expNN(natTen, nat{Word(prec)}, nil) + r = r.mul(r, p) + r, r2 := r.div(nat{}, r, z.b) + + // See if we need to round up + r2 = r2.mul(r2, natTwo) + if z.b.cmp(r2) <= 0 { + r = r.add(r, natOne) + } + + rs := r.string(10) + leadingZeros := prec - len(rs) + s += "." + strings.Repeat("0", leadingZeros) + rs + s = strings.TrimRight(s, "0") + + return s +} diff --git a/src/pkg/big/rat_test.go b/src/pkg/big/rat_test.go new file mode 100644 index 0000000000..cd47cbd553 --- /dev/null +++ b/src/pkg/big/rat_test.go @@ -0,0 +1,185 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package big + +import "testing" + + +type setStringTest struct { + in, out string +} + +var setStringTests = []setStringTest{ + setStringTest{"0", "0"}, + setStringTest{"1", "1"}, + setStringTest{"-1", "-1"}, + setStringTest{"2/4", "1/2"}, + setStringTest{".25", "1/4"}, + setStringTest{"-1/5", "-1/5"}, +} + +func TestRatSetString(t *testing.T) { + for i, test := range setStringTests { + x, _ := new(Rat).SetString(test.in) + + if x.String() != test.out { + t.Errorf("#%d got %s want %s", i, x.String(), test.out) + } + } +} + + +type floatStringTest struct { + in string + prec int + out string +} + +var floatStringTests = []floatStringTest{ + floatStringTest{"0", 0, "0"}, + floatStringTest{"0", 4, "0"}, + floatStringTest{"1", 0, "1"}, + floatStringTest{"1", 2, "1"}, + floatStringTest{"-1", 0, "-1"}, + floatStringTest{".25", 2, "0.25"}, + floatStringTest{".25", 1, "0.3"}, + floatStringTest{"-1/3", 3, "-0.333"}, + floatStringTest{"-2/3", 4, "-0.6667"}, +} + +func TestFloatString(t *testing.T) { + for i, test := range floatStringTests { + x, _ := new(Rat).SetString(test.in) + + if x.FloatString(test.prec) != test.out { + t.Errorf("#%d got %s want %s", i, x.FloatString(test.prec), test.out) + } + } +} + + +type ratCmpTest struct { + rat1, rat2 string + out int +} + +var ratCmpTests = []ratCmpTest{ + ratCmpTest{"0", "0/1", 0}, + ratCmpTest{"1/1", "1", 0}, + ratCmpTest{"-1", "-2/2", 0}, + ratCmpTest{"1", "0", 1}, + ratCmpTest{"0/1", "1/1", -1}, + ratCmpTest{"-5/1434770811533343057144", "-5/1434770811533343057145", -1}, + ratCmpTest{"49832350382626108453/8964749413", "49832350382626108454/8964749413", -1}, + ratCmpTest{"-37414950961700930/7204075375675961", "37414950961700930/7204075375675961", -1}, + ratCmpTest{"37414950961700930/7204075375675961", "74829901923401860/14408150751351922", 0}, +} + +func TestRatCmp(t *testing.T) { + for i, test := range ratCmpTests { + x, _ := new(Rat).SetString(test.rat1) + y, _ := new(Rat).SetString(test.rat2) + + out := x.Cmp(y) + if out != test.out { + t.Errorf("#%d got out = %v; want %v", i, out, test.out) + } + } +} + + +type ratBinFun func(z, x, y *Rat) *Rat +type ratBinArg struct { + x string + y string + out string +} + +func testRatBin(t *testing.T, f ratBinFun, a []ratBinArg) { + for i, test := range a { + x, _ := NewRat(0, 1).SetString(test.x) + y, _ := NewRat(0, 1).SetString(test.y) + expected, _ := NewRat(0, 1).SetString(test.out) + out := f(NewRat(0, 1), x, y) + + if out.Cmp(expected) != 0 { + t.Errorf("#%d got %s want %s", i, out, expected) + } + } +} + + +var ratAddTests = []ratBinArg{ + ratBinArg{"0", "0", "0"}, + ratBinArg{"0", "1", "1"}, + ratBinArg{"-1", "0", "-1"}, + ratBinArg{"-1", "1", "0"}, + ratBinArg{"1", "1", "2"}, + ratBinArg{"1/2", "1/2", "1"}, + ratBinArg{"1/4", "1/3", "7/12"}, + ratBinArg{"2/5", "-14/3", "-64/15"}, + ratBinArg{"4707/49292519774798173060", "-3367/70976135186689855734", "84058377121001851123459/1749296273614329067191168098769082663020"}, + ratBinArg{"-61204110018146728334/3", "-31052192278051565633/2", "-215564796870448153567/6"}, +} + +func TestRatAdd(t *testing.T) { + testRatBin(t, (*Rat).Add, ratAddTests) +} + + +var ratSubTests = []ratBinArg{ + ratBinArg{"0", "0", "0"}, + ratBinArg{"0", "1", "-1"}, + ratBinArg{"-1", "0", "-1"}, + ratBinArg{"-1", "1", "-2"}, + ratBinArg{"1", "1", "0"}, + ratBinArg{"1/2", "1/3", "1/6"}, + ratBinArg{"1/4", "1/3", "-1/12"}, + ratBinArg{"2/5", "-14/3", "76/15"}, + ratBinArg{"4707/49292519774798173060", "-3367/70976135186689855734", "250026291202747299816479/1749296273614329067191168098769082663020"}, + ratBinArg{"-27/133467566250814981", "-18/31750379913563777419", "-854857841473707320655/4237645934602118692642972629634714039"}, + ratBinArg{"27674141753240653/30123979153216", "-19948846211000086/637313996471", "618575745270541348005638912139/19198433543745179392300736"}, +} + +func TestRatSub(t *testing.T) { + testRatBin(t, (*Rat).Sub, ratSubTests) +} + + +var ratMulTests = []ratBinArg{ + ratBinArg{"0", "0", "0"}, + ratBinArg{"0", "1", "0"}, + ratBinArg{"-1", "0", "0"}, + ratBinArg{"-1", "1", "-1"}, + ratBinArg{"1", "1", "1"}, + ratBinArg{"1/2", "1/2", "1/4"}, + ratBinArg{"1/4", "1/3", "1/12"}, + ratBinArg{"2/5", "-14/3", "-28/15"}, + ratBinArg{"-3/26206484091896184128", "5/2848423294177090248", "-5/24882386581946146755650075889827061248"}, + ratBinArg{"26946729/330400702820", "41563965/225583428284", "224002580204097/14906584649915733312176"}, + ratBinArg{"-8259900599013409474/7", "-84829337473700364773/56707961321161574960", "350340947706464153265156004876107029701/198477864624065512360"}, +} + +func TestRatMul(t *testing.T) { + testRatBin(t, (*Rat).Mul, ratMulTests) +} + + +var ratQuoTests = []ratBinArg{ + ratBinArg{"0", "1", "0"}, + ratBinArg{"0", "-1", "0"}, + ratBinArg{"-1", "1", "-1"}, + ratBinArg{"1", "1", "1"}, + ratBinArg{"1/2", "1/2", "1"}, + ratBinArg{"1/4", "1/3", "3/4"}, + ratBinArg{"2/5", "-14/3", "-3/35"}, + ratBinArg{"808/45524274987585732633", "29/712593081308", "575775209696864/1320203974639986246357"}, + ratBinArg{"8967230/3296219033", "6269770/1992362624741777", "1786597389946320496771/2066653520653241"}, + ratBinArg{"-3784609207827/3426986245", "9381566963714/9633539", "-36459180403360509753/32150500941194292113930"}, +} + +func TestRatQuo(t *testing.T) { + testRatBin(t, (*Rat).Quo, ratQuoTests) +}