| // 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) |
| } |
| } |