| // Copyright 2009 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. | |
| // A little test program and benchmark for rational arithmetics. | |
| // Computes a Hilbert matrix, its inverse, multiplies them | |
| // and verifies that the product is the identity matrix. | |
| package big | |
| import ( | |
| "fmt" | |
| "testing" | |
| ) | |
| type matrix struct { | |
| n, m int | |
| a []*Rat | |
| } | |
| func (a *matrix) at(i, j int) *Rat { | |
| if !(0 <= i && i < a.n && 0 <= j && j < a.m) { | |
| panic("index out of range") | |
| } | |
| return a.a[i*a.m+j] | |
| } | |
| func (a *matrix) set(i, j int, x *Rat) { | |
| if !(0 <= i && i < a.n && 0 <= j && j < a.m) { | |
| panic("index out of range") | |
| } | |
| a.a[i*a.m+j] = x | |
| } | |
| func newMatrix(n, m int) *matrix { | |
| if !(0 <= n && 0 <= m) { | |
| panic("illegal matrix") | |
| } | |
| a := new(matrix) | |
| a.n = n | |
| a.m = m | |
| a.a = make([]*Rat, n*m) | |
| return a | |
| } | |
| func newUnit(n int) *matrix { | |
| a := newMatrix(n, n) | |
| for i := 0; i < n; i++ { | |
| for j := 0; j < n; j++ { | |
| x := NewRat(0, 1) | |
| if i == j { | |
| x.SetInt64(1) | |
| } | |
| a.set(i, j, x) | |
| } | |
| } | |
| return a | |
| } | |
| func newHilbert(n int) *matrix { | |
| a := newMatrix(n, n) | |
| for i := 0; i < n; i++ { | |
| for j := 0; j < n; j++ { | |
| a.set(i, j, NewRat(1, int64(i+j+1))) | |
| } | |
| } | |
| return a | |
| } | |
| func newInverseHilbert(n int) *matrix { | |
| a := newMatrix(n, n) | |
| for i := 0; i < n; i++ { | |
| for j := 0; j < n; j++ { | |
| x1 := new(Rat).SetInt64(int64(i + j + 1)) | |
| x2 := new(Rat).SetInt(new(Int).Binomial(int64(n+i), int64(n-j-1))) | |
| x3 := new(Rat).SetInt(new(Int).Binomial(int64(n+j), int64(n-i-1))) | |
| x4 := new(Rat).SetInt(new(Int).Binomial(int64(i+j), int64(i))) | |
| x1.Mul(x1, x2) | |
| x1.Mul(x1, x3) | |
| x1.Mul(x1, x4) | |
| x1.Mul(x1, x4) | |
| if (i+j)&1 != 0 { | |
| x1.Neg(x1) | |
| } | |
| a.set(i, j, x1) | |
| } | |
| } | |
| return a | |
| } | |
| func (a *matrix) mul(b *matrix) *matrix { | |
| if a.m != b.n { | |
| panic("illegal matrix multiply") | |
| } | |
| c := newMatrix(a.n, b.m) | |
| for i := 0; i < c.n; i++ { | |
| for j := 0; j < c.m; j++ { | |
| x := NewRat(0, 1) | |
| for k := 0; k < a.m; k++ { | |
| x.Add(x, new(Rat).Mul(a.at(i, k), b.at(k, j))) | |
| } | |
| c.set(i, j, x) | |
| } | |
| } | |
| return c | |
| } | |
| func (a *matrix) eql(b *matrix) bool { | |
| if a.n != b.n || a.m != b.m { | |
| return false | |
| } | |
| for i := 0; i < a.n; i++ { | |
| for j := 0; j < a.m; j++ { | |
| if a.at(i, j).Cmp(b.at(i, j)) != 0 { | |
| return false | |
| } | |
| } | |
| } | |
| return true | |
| } | |
| func (a *matrix) String() string { | |
| s := "" | |
| for i := 0; i < a.n; i++ { | |
| for j := 0; j < a.m; j++ { | |
| s += fmt.Sprintf("\t%s", a.at(i, j)) | |
| } | |
| s += "\n" | |
| } | |
| return s | |
| } | |
| func doHilbert(t *testing.T, n int) { | |
| a := newHilbert(n) | |
| b := newInverseHilbert(n) | |
| I := newUnit(n) | |
| ab := a.mul(b) | |
| if !ab.eql(I) { | |
| if t == nil { | |
| panic("Hilbert failed") | |
| } | |
| t.Errorf("a = %s\n", a) | |
| t.Errorf("b = %s\n", b) | |
| t.Errorf("a*b = %s\n", ab) | |
| t.Errorf("I = %s\n", I) | |
| } | |
| } | |
| func TestHilbert(t *testing.T) { | |
| doHilbert(t, 10) | |
| } | |
| func BenchmarkHilbert(b *testing.B) { | |
| for i := 0; i < b.N; i++ { | |
| doHilbert(nil, 10) | |
| } | |
| } |