init: v1.0.0

This commit is contained in:
yaole
2026-05-27 23:03:00 +08:00
commit 8d97f750eb
466 changed files with 80067 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2012 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+104
View File
@@ -0,0 +1,104 @@
Gmp
===
[![GoDoc](https://godoc.org/github.com/ncw/gmp?status.svg)](https://godoc.org/github.com/ncw/gmp)
[![Build Status](https://travis-ci.org/ncw/gmp.svg?branch=master)](https://travis-ci.org/ncw/gmp)
This package provides a drop in replacement for Go's built in
[math/big](http://golang.org/pkg/math/big/) big integer package using
the [GNU Multiprecision Library](http://gmplib.org/) (GMP).
GMP is very much faster than Go's math/big however it is an external C
library with all the problems that entails (cgo, dependencies etc)
This library was made by taking the [cgo example of wrapping
GMP](http://golang.org/misc/cgo/gmp/gmp.go) from the Go source and
doing the following to it
* Copying the implementation from misc/cgo/gmp/gmp.go
* Copying the documentation from src/pkg/math/big/int.go
* Additional implementation of missing methods
* Bug fixes for existing implementations
* Making it passes the test suite from src/pkg/math/big/int_test.go
* Adding memory management
* Fix problems on 32 bit platforms when using `int64` values which don't fit into a `C.long`
* Implementing Rat support making it pass src/pkg/math/big/rat_test.go
See here for package docs
* https://godoc.org/github.com/ncw/gmp
Install
-------
Use go to install the library
go get github.com/ncw/gmp
Usage
-----
See here for full package docs
* http://go.pkgdoc.org/github.com/ncw/gmp
To use as in a drop in replacement for math/big, replace
import "math/big"
With
import big "github.com/ncw/gmp"
Features that aren't part of math/big are clearly marked and if you
are using those, then I suggest you import as
import "github.com/ncw/gmp"
Testing
-------
To run the tests use
go test github.com/ncw/gmp
The tests have been copied from the tests for the math/big library in
the Go source and modified as little as possible so it should be 100%
compatible.
Differences
-----------
Here are the differences between math/big and this package
* `Int.Bits` and `Int.SetBits` not implemented
* `Rat.Num()` and `Rat.Denom()` return a copy not a reference, so
* If you want to set them use the new methods `Rat.SetNum()` and `Rat.SetDenom()`
License
-------
As this contains a great deal of code copied from the Go source it is
licenced identically to the Go source itself - see the LICENSE file
for details.
Contact and support
-------------------
The project website is at:
* https://github.com/ncw/gmp
There you can file bug reports, ask for help or contribute patches.
Authors
-------
* [The Go team](http://golang.org/AUTHORS)
* Nick Craig-Wood <nick@craig-wood.com>
Contributors
------------
* Bert Gijsbers <gijhub@gmail.com>
+140
View File
@@ -0,0 +1,140 @@
//go:build gmp
// +build gmp
package gmp
import (
"testing"
)
func BenchmarkSwapO(bench *testing.B) {
a := NewInt(1)
b := NewInt(2)
c := NewInt(0)
for i := bench.N; i > 0; i-- {
c.Set(a)
a.Set(b)
b.Set(c)
}
}
func BenchmarkSwapX(bench *testing.B) {
a := NewInt(1)
b := NewInt(2)
for i := bench.N; i > 0; i-- {
a.Swap(b)
}
}
func BenchmarkAddUintO(bench *testing.B) {
a := NewInt(1)
b := NewInt(2)
for i := bench.N; i > 0; i-- {
b.SetUint64(uint64(i))
a.Add(a, b)
}
}
func BenchmarkAddUintX(bench *testing.B) {
a := NewInt(1)
for i := bench.N; i > 0; i-- {
a.AddUint32(a, uint32(i))
}
}
func BenchmarkMulInt0(bench *testing.B) {
a := NewInt(1)
b := NewInt(2)
for i := bench.N; i > 0; i-- {
b.SetInt64(1)
a.Mul(a, b)
}
}
func BenchmarkMulIntX(bench *testing.B) {
a := NewInt(1)
for i := bench.N; i > 0; i-- {
a.MulInt32(a, 1)
}
}
func BenchmarkAddMulO(bench *testing.B) {
a := NewInt(1)
b := NewInt(2)
c := NewInt(0)
for i := bench.N; i > 0; i-- {
a.Add(a, c)
b.Mul(b, a)
}
}
func BenchmarkAddMulX(bench *testing.B) {
a := NewInt(1)
b := NewInt(2)
c := NewInt(0)
for i := bench.N; i > 0; i-- {
b.AddMul(a, c)
}
}
func BenchmarkCmpIntO(bench *testing.B) {
a := NewInt(1)
b := NewInt(2)
for i := bench.N; i > 0; i-- {
a.SetInt64(int64(i))
b.Cmp(a)
}
}
func BenchmarkCmpIntX(bench *testing.B) {
a := NewInt(1)
for i := bench.N; i > 0; i-- {
a.CmpInt32(int32(i))
}
}
func BenchmarkCmpAbsO(bench *testing.B) {
a := NewInt(1)
b := NewInt(2)
for i := bench.N; i > 0; i-- {
a.Abs(a)
b.Abs(b)
a.Cmp(b)
}
}
func BenchmarkCmpAbsX(bench *testing.B) {
a := NewInt(1)
b := NewInt(1)
for i := bench.N; i > 0; i-- {
a.CmpAbs(b)
}
}
func BenchmarkGetIntO(bench *testing.B) {
a := NewInt(1)
for i := bench.N; i > 0; i-- {
a.Int64()
}
}
func BenchmarkGetIntX(bench *testing.B) {
a := NewInt(1)
for i := bench.N; i > 0; i-- {
a.Int32()
}
}
func BenchmarkGetUintO(bench *testing.B) {
a := NewInt(1)
for i := bench.N; i > 0; i-- {
a.Uint64()
}
}
func BenchmarkGetUintX(bench *testing.B) {
a := NewInt(1)
for i := bench.N; i > 0; i-- {
a.Uint32()
}
}
+104
View File
@@ -0,0 +1,104 @@
//go:build gmp
// +build gmp
package gmp
import (
"fmt"
"testing"
"time"
)
// Make a n digit number
func nDigitNumberGmp(digits int64) *Int {
x := NewInt(10)
n := NewInt(digits)
one := NewInt(1)
x.Exp(x, n, nil)
x.Sub(x, one)
return x
}
func TestPow(t *testing.T) {
n, _ := NewInt(1).SetString("151787717184422252307365020658866150659139813606887864663574443176328014948429913407344055128026662986925949147541666619511526142491196760869802510776788353419093645990851372578901256051814507665042918937343846064086867942964225457691185973174205523727877484153730596121423900155549117133518141928295347285789", 10)
m, _ := NewInt(1).SetString("15178771718442225230736502065886615065913981360688786466357444317632801494842991340734405512802666298692594914754166661951152614249119676086980251077678835341909364599085137257890125605181450766504291893734384606408686794296422545769118597317420552372787748415373059612142390015554911713351814192829534728578", 10)
d, _ := NewInt(1).SetString("141787717184422252307365020658866150659139813606887864663574443176328014948429913407344055128026662986925949147541666619511526142491196760869802510776788353419093645990851372578901256051814507665042918937343846064086867942964225457691185973174205523727877484153730596121423900155549117133518141928295347285789", 10)
c := NewInt(1)
cnt := 10000
start := time.Now()
for i := 0; i < cnt; i++ {
c.Exp(m, d, n)
}
elapsed := time.Since(start)
fmt.Printf("Sign %d times\n", cnt)
fmt.Printf("Used time: %d ms, %d pcs/s\n", elapsed.Milliseconds(), int(float64(cnt)/float64(elapsed.Milliseconds())*1000))
}
func benchmarkGmpAddN(b *testing.B, digits int64) {
x := nDigitNumberGmp(digits)
y := nDigitNumberGmp(digits)
z := NewInt(0)
b.ResetTimer()
b.StartTimer()
for i := b.N - 1; i >= 0; i-- {
z.Add(x, y)
}
}
func BenchmarkGmpAdd1(b *testing.B) {
benchmarkGmpAddN(b, 10)
}
func BenchmarkGmpAdd10(b *testing.B) {
benchmarkGmpAddN(b, 10)
}
func BenchmarkGmpAdd100(b *testing.B) {
benchmarkGmpAddN(b, 100)
}
func BenchmarkGmpAdd1000(b *testing.B) {
benchmarkGmpAddN(b, 1000)
}
func BenchmarkGmpAdd10000(b *testing.B) {
benchmarkGmpAddN(b, 10000)
}
func BenchmarkGmpAdd100000(b *testing.B) {
benchmarkGmpAddN(b, 100000)
}
func BenchmarkGmpAdd1000000(b *testing.B) {
benchmarkGmpAddN(b, 1000000)
}
func benchmarkGmpMulN(b *testing.B, digits int64) {
x := nDigitNumberGmp(digits)
y := nDigitNumberGmp(digits)
z := NewInt(0)
b.ResetTimer()
b.StartTimer()
for i := b.N - 1; i >= 0; i-- {
z.Mul(x, y)
}
}
func BenchmarkGmpMul1(b *testing.B) {
benchmarkGmpMulN(b, 10)
}
func BenchmarkGmpMul10(b *testing.B) {
benchmarkGmpMulN(b, 10)
}
func BenchmarkGmpMul100(b *testing.B) {
benchmarkGmpMulN(b, 100)
}
func BenchmarkGmpMul1000(b *testing.B) {
benchmarkGmpMulN(b, 1000)
}
func BenchmarkGmpMul10000(b *testing.B) {
benchmarkGmpMulN(b, 10000)
}
func BenchmarkGmpMul100000(b *testing.B) {
benchmarkGmpMulN(b, 100000)
}
func BenchmarkGmpMul1000000(b *testing.B) {
benchmarkGmpMulN(b, 1000000)
}
+85
View File
@@ -0,0 +1,85 @@
//go:build gmp
// +build gmp
package gmp_test
import (
"math/big"
"testing"
)
// Make a n digit number
func nDigitNumberMathBig(digits int64) *big.Int {
x := big.NewInt(10)
n := big.NewInt(digits)
one := big.NewInt(1)
x.Exp(x, n, nil)
x.Sub(x, one)
return x
}
func benchmarkMathBigAddN(b *testing.B, digits int64) {
x := nDigitNumberMathBig(digits)
y := nDigitNumberMathBig(digits)
z := big.NewInt(0)
b.ResetTimer()
b.StartTimer()
for i := b.N - 1; i >= 0; i-- {
z.Add(x, y)
}
}
func BenchmarkMathBigAdd1(b *testing.B) {
benchmarkMathBigAddN(b, 10)
}
func BenchmarkMathBigAdd10(b *testing.B) {
benchmarkMathBigAddN(b, 10)
}
func BenchmarkMathBigAdd100(b *testing.B) {
benchmarkMathBigAddN(b, 100)
}
func BenchmarkMathBigAdd1000(b *testing.B) {
benchmarkMathBigAddN(b, 1000)
}
func BenchmarkMathBigAdd10000(b *testing.B) {
benchmarkMathBigAddN(b, 10000)
}
func BenchmarkMathBigAdd100000(b *testing.B) {
benchmarkMathBigAddN(b, 100000)
}
func BenchmarkMathBigAdd1000000(b *testing.B) {
benchmarkMathBigAddN(b, 1000000)
}
func benchmarkMathBigMulN(b *testing.B, digits int64) {
x := nDigitNumberMathBig(digits)
y := nDigitNumberMathBig(digits)
z := big.NewInt(0)
b.ResetTimer()
b.StartTimer()
for i := b.N - 1; i >= 0; i-- {
z.Mul(x, y)
}
}
func BenchmarkMathBigMul1(b *testing.B) {
benchmarkMathBigMulN(b, 10)
}
func BenchmarkMathBigMul10(b *testing.B) {
benchmarkMathBigMulN(b, 10)
}
func BenchmarkMathBigMul100(b *testing.B) {
benchmarkMathBigMulN(b, 100)
}
func BenchmarkMathBigMul1000(b *testing.B) {
benchmarkMathBigMulN(b, 1000)
}
func BenchmarkMathBigMul10000(b *testing.B) {
benchmarkMathBigMulN(b, 10000)
}
func BenchmarkMathBigMul100000(b *testing.B) {
benchmarkMathBigMulN(b, 100000)
}
func BenchmarkMathBigMul1000000(b *testing.B) {
benchmarkMathBigMulN(b, 1000000)
}
+30
View File
@@ -0,0 +1,30 @@
// 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.
// Package gmp implements multi-precision arithmetic (big numbers).
//
// This package provides a drop in replacement for Go's built in
// math/big integer package using the GNU Multiprecision Library (GMP)
// to implement the operations.
//
// GMP is very much faster than Go's math/big however it is an
// external C library with all the problems that entails (cgo,
// dependencies etc)
//
// The following numeric types are supported:
//
// - Int signed integers
// - Rat rational numbers are NOT yet supported
//
// Methods are typically of the form:
//
// func (z *Int) Op(x, y *Int) *Int (similar for *Rat)
//
// and implement operations z = x Op y with the result as receiver; if it
// is one of the operands it may be overwritten (and its memory reused).
// To enable chaining of operations, the result is also returned. Methods
// returning a result other than *Int or *Rat take one of the operands as
// the receiver.
//
package gmp
+53
View File
@@ -0,0 +1,53 @@
// Copyright 2012 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.
//go:build gmp
// +build gmp
package gmp
import (
"fmt"
"log"
)
// func ExampleRat_SetString() {
// r := new(big.Rat)
// r.SetString("355/113")
// fmt.Println(r.FloatString(3))
// // Output: 3.142
// }
func ExampleInt_SetString() {
i := new(Int)
i.SetString("644", 8) // octal
fmt.Println(i)
// Output: 420
}
// func ExampleRat_Scan() {
// // The Scan function is rarely used directly;
// // the fmt package recognizes it as an implementation of fmt.Scanner.
// r := new(Rat)
// _, err := fmt.Sscan("1.5000", r)
// if err != nil {
// log.Println("error scanning value:", err)
// } else {
// fmt.Println(r)
// }
// // Output: 3/2
// }
func ExampleInt_Scan() {
// The Scan function is rarely used directly;
// the fmt package recognizes it as an implementation of fmt.Scanner.
i := new(Int)
_, err := fmt.Sscan("18446744073709551617", i)
if err != nil {
log.Println("error scanning value:", err)
} else {
fmt.Println(i)
}
// Output: 18446744073709551617
}
+957
View File
@@ -0,0 +1,957 @@
// 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.
// This file implements signed multi-precision integers.
// +build gmp
package gmp
// FIXME could we use Go's allocator (gmp can use a custom allocator)
// instead of using runtime.SetFinalizer to manage memory?
/*
#cgo LDFLAGS: -lgmp
#include <gmp.h>
#include <stdlib.h>
// gmp 5.0.0+ changed the type of the 3rd argument to mp_bitcnt_t,
// so, to support older versions, we wrap these two functions.
void _mpz_mul_2exp(mpz_ptr a, mpz_ptr b, unsigned long n) {
mpz_mul_2exp(a, b, n);
}
void _mpz_div_2exp(mpz_ptr a, mpz_ptr b, unsigned long n) {
mpz_div_2exp(a, b, n);
}
unsigned int _mpz_tstbit(mpz_ptr a, unsigned long n) {
return mpz_tstbit(a, n);
}
void _mpz_clrbit(mpz_ptr a, unsigned long n) {
mpz_clrbit(a, n);
}
void _mpz_setbit(mpz_ptr a, unsigned long n) {
mpz_setbit(a, n);
}
// Macros made into functions
int _mpz_sgn(mpz_t op) {
return mpz_sgn(op);
}
*/
import "C"
import (
"errors"
"fmt"
"io"
"math/rand"
"runtime"
"strings"
"unicode"
"unsafe"
)
// Some definited Ints for internal use only
var (
_Int0 = NewInt(0)
_Int1 = NewInt(1)
_Int10 = NewInt(10)
)
// An Int represents a signed multi-precision integer.
// The zero value for an Int represents the value 0.
type Int struct {
i C.mpz_t
init bool
}
// Finalizer - release the memory allocated to the mpz
func intFinalize(z *Int) {
if z.init {
runtime.SetFinalizer(z, nil)
C.mpz_clear(&z.i[0])
z.init = false
}
}
// Int promises that the zero value is a 0, but in gmp
// the zero value is a crash. To bridge the gap, the
// init bool says whether this is a valid gmp value.
// doinit initializes z.i if it needs it.
func (z *Int) doinit() {
if z.init {
return
}
z.init = true
C.mpz_init(&z.i[0])
runtime.SetFinalizer(z, intFinalize)
}
// Clear the allocated space used by the number
//
// This normally happens on a runtime.SetFinalizer call, but if you
// want immediate deallocation you can call it.
//
// NB This is not part of big.Int
func (z *Int) Clear() {
intFinalize(z)
}
// Sign returns:
//
// -1 if x < 0
// 0 if x == 0
// +1 if x > 0
//
func (z *Int) Sign() int {
z.doinit()
return int(C._mpz_sgn(&z.i[0]))
}
// SetInt64 sets z to x and returns z.
func (z *Int) SetInt64(x int64) *Int {
z.doinit()
// Test for truncation
y := C.long(x)
if int64(y) == x {
C.mpz_set_si(&z.i[0], y)
} else {
negative := false
if x < 0 {
x = -x
negative = true
}
C.mpz_import(&z.i[0], 1, 0, 8, 0, 0, unsafe.Pointer(&x))
if negative {
C.mpz_neg(&z.i[0], &z.i[0])
}
}
return z
}
// SetUint64 sets z to x and returns z.
func (z *Int) SetUint64(x uint64) *Int {
z.doinit()
// Test for truncation
y := C.ulong(x)
if uint64(y) == x {
C.mpz_set_ui(&z.i[0], y)
} else {
C.mpz_import(&z.i[0], 1, 0, 8, 0, 0, unsafe.Pointer(&x))
}
return z
}
// NewInt allocates and returns a new Int set to x.
func NewInt(x int64) *Int {
return new(Int).SetInt64(x)
}
// Set sets z to x and returns z.
func (z *Int) Set(x *Int) *Int {
z.doinit()
C.mpz_set(&z.i[0], &x.i[0])
return z
}
// Bits provides raw (unchecked but fast) access to x by returning its
// absolute value as a little-endian Word slice. The result and x share
// the same underlying array.
// Bits is intended to support implementation of missing low-level Int
// functionality outside this package; it should be avoided otherwise.
// func (z *Int) Bits() []Word {
// // FIXME not implemented
// return nil
// }
// SetBits provides raw (unchecked but fast) access to z by setting its
// value to abs, interpreted as a little-endian Word slice, and returning
// z. The result and abs share the same underlying array.
// SetBits is intended to support implementation of missing low-level Int
// functionality outside this package; it should be avoided otherwise.
// func (z *Int) SetBits(abs []Word) *Int {
// // FIXME not implemented
// return nil
// }
// Abs sets z to |x| (the absolute value of x) and returns z.
func (z *Int) Abs(x *Int) *Int {
x.doinit()
z.doinit()
C.mpz_abs(&z.i[0], &x.i[0])
return z
}
// Neg sets z to -x and returns z.
func (z *Int) Neg(x *Int) *Int {
x.doinit()
z.doinit()
C.mpz_neg(&z.i[0], &x.i[0])
return z
}
// Add sets z to the sum x+y and returns z.
func (z *Int) Add(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
C.mpz_add(&z.i[0], &x.i[0], &y.i[0])
return z
}
// Sub sets z to the difference x-y and returns z.
func (z *Int) Sub(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
C.mpz_sub(&z.i[0], &x.i[0], &y.i[0])
return z
}
// Mul sets z to the product x*y and returns z.
func (z *Int) Mul(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
C.mpz_mul(&z.i[0], &x.i[0], &y.i[0])
return z
}
// MulRange sets z to the product of all integers
// in the range [a, b] inclusively and returns z.
// If a > b (empty range), the result is 1.
func (z *Int) MulRange(a, b int64) *Int {
switch {
case a > b:
return z.SetInt64(1) // empty range
case a <= 0 && b >= 0:
return z.SetInt64(0) // range includes 0
}
// a <= b && (b < 0 || a > 0)
// Can use gmp factorial routine if a = 1 and b >= 1
if a == 1 && b >= 1 {
C.mpz_fac_ui(&z.i[0], C.ulong(b))
} else {
// Slow
z.SetInt64(a)
for i := a + 1; i <= b; i++ {
C.mpz_mul_si(&z.i[0], &z.i[0], C.long(i))
}
}
return z
}
// Binomial sets z to the binomial coefficient of (n, k) and returns z.
func (z *Int) Binomial(n, k int64) *Int {
var a, b Int
a.MulRange(n-k+1, n)
b.MulRange(1, k)
return z.Quo(&a, &b)
}
// Quo sets z to the quotient x/y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs.
// Quo implements truncated division (like Go); see QuoRem for more details.
func (z *Int) Quo(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
C.mpz_tdiv_q(&z.i[0], &x.i[0], &y.i[0])
return z
}
// Rem sets z to the remainder x%y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs.
// Rem implements truncated modulus (like Go); see QuoRem for more details.
func (z *Int) Rem(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
C.mpz_tdiv_r(&z.i[0], &x.i[0], &y.i[0])
return z
}
// QuoRem sets z to the quotient x/y and r to the remainder x%y
// and returns the pair (z, r) for y != 0.
// If y == 0, a division-by-zero run-time panic occurs.
//
// QuoRem implements T-division and modulus (like Go):
//
// q = x/y with the result truncated to zero
// r = x - y*q
//
// (See Daan Leijen, ``Division and Modulus for Computer Scientists''.)
// See DivMod for Euclidean division and modulus (unlike Go).
//
func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) {
x.doinit()
y.doinit()
r.doinit()
z.doinit()
C.mpz_tdiv_qr(&z.i[0], &r.i[0], &x.i[0], &y.i[0])
return z, r
}
// Div sets z to the quotient x/y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs.
// Div implements Euclidean division (unlike Go); see DivMod for more details.
func (z *Int) Div(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
switch y.Sign() {
case 1:
C.mpz_fdiv_q(&z.i[0], &x.i[0], &y.i[0])
case -1:
C.mpz_cdiv_q(&z.i[0], &x.i[0], &y.i[0])
case 0:
panic("Division by zero")
}
return z
}
// Mod sets z to the modulus x%y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs.
// Mod implements Euclidean modulus (unlike Go); see DivMod for more details.
func (z *Int) Mod(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
switch y.Sign() {
case 1:
C.mpz_fdiv_r(&z.i[0], &x.i[0], &y.i[0])
case -1:
C.mpz_cdiv_r(&z.i[0], &x.i[0], &y.i[0])
case 0:
panic("Division by zero")
}
return z
}
// DivMod sets z to the quotient x div y and m to the modulus x mod y
// and returns the pair (z, m) for y != 0.
// If y == 0, a division-by-zero run-time panic occurs.
//
// DivMod implements Euclidean division and modulus (unlike Go):
//
// q = x div y such that
// m = x - y*q with 0 <= m < |q|
//
// (See Raymond T. Boute, ``The Euclidean definition of the functions
// div and mod''. ACM Transactions on Programming Languages and
// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992.
// ACM press.)
// See QuoRem for T-division and modulus (like Go).
//
func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) {
x.doinit()
y.doinit()
m.doinit()
z.doinit()
switch y.Sign() {
case 1:
C.mpz_fdiv_qr(&z.i[0], &m.i[0], &x.i[0], &y.i[0])
case -1:
C.mpz_cdiv_qr(&z.i[0], &m.i[0], &x.i[0], &y.i[0])
case 0:
panic("Division by zero")
}
return z, m
}
// Cmp compares z and y and returns:
//
// -1 if z < y
// 0 if z == y
// +1 if z > y
//
func (z *Int) Cmp(y *Int) (r int) {
z.doinit()
y.doinit()
r = int(C.mpz_cmp(&z.i[0], &y.i[0]))
if r < 0 {
r = -1
} else if r > 0 {
r = 1
}
return
}
// string returns z in the base given
func (z *Int) string(base int) string {
if z == nil {
return "<nil>"
}
z.doinit()
p := C.mpz_get_str(nil, C.int(base), &z.i[0])
s := C.GoString(p)
C.free(unsafe.Pointer(p))
return s
}
// String returns the decimal representation of z.
func (z *Int) String() string {
return z.string(10)
}
// Convert rune into base
//
// Note gmp says -ve bases make upper case
func baseForRune(ch rune) int {
switch ch {
case 'b':
return 2
case 'o':
return 8
case 'd', 's', 'v':
return 10
case 'x':
return 16
case 'X':
return -16
}
return 0 // unknown format
}
// write count copies of text to s
func writeMultiple(s fmt.State, text string, count int) {
if len(text) > 0 {
b := []byte(text)
for ; count > 0; count-- {
_, _ = s.Write(b)
}
}
}
// Format is a support routine for fmt.Formatter. It accepts
// the formats 'b' (binary), 'o' (octal), 'd' (decimal), 'x'
// (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
// Also supported are the full suite of package fmt's format
// verbs for integral types, including '+', '-', and ' '
// for sign control, '#' for leading zero in octal and for
// hexadecimal, a leading "0x" or "0X" for "%#x" and "%#X"
// respectively, specification of minimum digits precision,
// output field width, space or zero padding, and left or
// right justification.
//
func (z *Int) Format(s fmt.State, ch rune) {
base := baseForRune(ch)
// special cases
switch {
case base == 0:
// unknown format
_, _ = fmt.Fprintf(s, "%%!%c(gmp.Int=%s)", ch, z.String())
return
case z == nil:
_, _ = fmt.Fprint(s, "<nil>")
return
}
// determine sign character
sign := ""
switch {
case z.Sign() < 0:
sign = "-"
case s.Flag('+'): // supersedes ' ' when both specified
sign = "+"
case s.Flag(' '):
sign = " "
}
// determine prefix characters for indicating output base
prefix := ""
if s.Flag('#') {
switch ch {
case 'o': // octal
prefix = "0"
case 'x': // hexadecimal
prefix = "0x"
case 'X':
prefix = "0X"
}
}
// determine digits with base set by len(cs) and digit characters from cs
digits := z.string(base)
if digits[0] == '-' {
digits = digits[1:]
}
// number of characters for the three classes of number padding
var left int // space characters to left of digits for right justification ("%8d")
var zeroes int // zero characters (actually cs[0]) as left-most digits ("%.8d")
var right int // space characters to right of digits for left justification ("%-8d")
// determine number padding from precision: the least number of digits to output
precision, precisionSet := s.Precision()
if precisionSet {
switch {
case len(digits) < precision:
zeroes = precision - len(digits) // count of zero padding
case digits == "0" && precision == 0:
return // print nothing if zero value (z == 0) and zero precision ("." or ".0")
}
}
// determine field pad from width: the least number of characters to output
length := len(sign) + len(prefix) + zeroes + len(digits)
if width, widthSet := s.Width(); widthSet && length < width { // pad as specified
switch d := width - length; {
case s.Flag('-'):
// pad on the right with spaces; supersedes '0' when both specified
right = d
case s.Flag('0') && !precisionSet:
// pad with zeroes unless precision also specified
zeroes = d
default:
// pad on the left with spaces
left = d
}
}
// print number as [left pad][sign][prefix][zero pad][digits][right pad]
writeMultiple(s, " ", left)
writeMultiple(s, sign, 1)
writeMultiple(s, prefix, 1)
writeMultiple(s, "0", zeroes)
writeMultiple(s, digits, 1)
writeMultiple(s, " ", right)
}
// Scan is a support routine for fmt.Scanner; it sets z to the value of
// the scanned number. It accepts the formats 'b' (binary), 'o' (octal),
// 'd' (decimal), 'x' (lowercase hexadecimal), and 'X' (uppercase hexadecimal).
func (z *Int) Scan(s fmt.ScanState, ch rune) error {
s.SkipSpace() // skip leading space characters
base := 0
switch ch {
case 'b':
base = 2
case 'o':
base = 8
case 'd':
base = 10
case 'x', 'X':
base = 16
case 's', 'v':
// let scan determine the base
default:
return errors.New("Int.Scan: invalid verb")
}
charset := "0123456789abcdef"
if base != 0 {
charset = charset[:base]
}
// Read the number into in
in := make([]byte, 0, 16)
var err error
var n int
for {
ch, n, err = s.ReadRune()
if err == io.EOF {
break
}
if err != nil {
return err
}
if n > 1 {
// Wide character - must be the end
err = s.UnreadRune()
if err != nil {
return err
}
break
}
ch = unicode.ToLower(ch)
if len(in) == 0 {
if ch == '+' {
// Skip leading + as gmp doesn't understand them
continue
}
if ch == '-' {
goto ok
}
}
if len(in) == 1 && base == 0 {
if ch == 'b' || ch == 'x' {
goto ok
}
}
if !strings.ContainsRune(charset, ch) {
// Bad character - end
err = s.UnreadRune()
if err != nil {
return err
}
break
}
ok:
in = append(in, byte(ch))
}
// Use GMP to convert it as it is very efficient for large numbers
z.doinit()
// null terminate for C
in = append(in, 0)
if C.mpz_set_str(&z.i[0], (*C.char)(unsafe.Pointer(&in[0])), C.int(base)) < 0 {
return errors.New("Int.Scan: failed")
}
return nil
}
// Int64 returns the int64 representation of z.
// If z cannot be represented in an int64, the result is undefined.
func (z *Int) Int64() (y int64) {
if !z.init {
return
}
if C.mpz_fits_slong_p(&z.i[0]) != 0 {
return int64(C.mpz_get_si(&z.i[0]))
}
// Undefined result if > 64 bits
if z.BitLen() > 64 {
return
}
C.mpz_export(unsafe.Pointer(&y), nil, -1, 8, 0, 0, &z.i[0])
if z.Sign() < 0 {
y = -y
}
return
}
// Uint64 returns the uint64 representation of z.
// If z cannot be represented in a uint64, the result is undefined.
func (z *Int) Uint64() (y uint64) {
if !z.init {
return
}
if C.mpz_fits_ulong_p(&z.i[0]) != 0 {
return uint64(C.mpz_get_ui(&z.i[0]))
}
// Undefined result if > 64 bits
if z.BitLen() > 64 {
return
}
C.mpz_export(unsafe.Pointer(&y), nil, -1, 8, 0, 0, &z.i[0])
return
}
// SetString sets z to the value of s, interpreted in the given base,
// and returns z and a boolean indicating success. If SetString fails,
// the value of z is undefined but the returned value is nil.
//
// The base argument must be 0 or a value from 2 through MaxBase. If the base
// is 0, the string prefix determines the actual conversion base. A prefix of
// ``0x'' or ``0X'' selects base 16; the ``0'' prefix selects base 8, and a
// ``0b'' or ``0B'' prefix selects base 2. Otherwise the selected base is 10.
//
func (z *Int) SetString(s string, base int) (*Int, bool) {
z.doinit()
if base != 0 && (base < 2 || base > 36) {
return nil, false
}
// Skip leading + as mpz_set_str doesn't understand them
if len(s) > 1 && s[0] == '+' {
s = s[1:]
}
// mpz_set_str incorrectly parses "0x" and "0b" as valid
if base == 0 && len(s) == 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X' || s[1] == 'b' || s[1] == 'B') {
return nil, false
}
p := C.CString(s)
defer C.free(unsafe.Pointer(p))
if C.mpz_set_str(&z.i[0], p, C.int(base)) < 0 {
return nil, false
}
return z, true // err == io.EOF => scan consumed all of s
}
// SetBytes interprets buf as the bytes of a big-endian unsigned
// integer, sets z to that value, and returns z.
func (z *Int) SetBytes(buf []byte) *Int {
z.doinit()
if len(buf) == 0 {
z.SetInt64(0)
} else {
C.mpz_import(&z.i[0], C.size_t(len(buf)), 1, 1, 1, 0, unsafe.Pointer(&buf[0]))
}
return z
}
// Bytes returns the absolute value of z as a big-endian byte slice.
func (z *Int) Bytes() []byte {
b := make([]byte, 1+(z.BitLen()+7)/8)
n := C.size_t(len(b))
C.mpz_export(unsafe.Pointer(&b[0]), &n, 1, 1, 1, 0, &z.i[0])
return b[0:n]
}
// BitLen returns the length of the absolute value of z in bits.
// The bit length of 0 is 0.
func (z *Int) BitLen() int {
z.doinit()
if z.Sign() == 0 {
return 0
}
return int(C.mpz_sizeinbase(&z.i[0], 2))
}
// Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z.
// If y <= 0, the result is 1; if m == nil or m == 0, z = x**y.
// See Knuth, volume 2, section 4.6.3.
func (z *Int) Exp(x, y, m *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
if y.Sign() <= 0 {
z.SetInt64(1)
return z
}
if m == nil || m.Sign() == 0 {
C.mpz_pow_ui(&z.i[0], &x.i[0], C.mpz_get_ui(&y.i[0]))
} else {
m.doinit()
C.mpz_powm(&z.i[0], &x.i[0], &y.i[0], &m.i[0])
}
return z
}
// GCD sets z to the greatest common divisor of a and b, which both must
// be > 0, and returns z.
// If x and y are not nil, GCD sets x and y such that z = a*x + b*y.
// If either a or b is <= 0, GCD sets z = x = y = 0.
func (z *Int) GCD(x, y, a, b *Int) *Int {
z.doinit()
a.doinit()
b.doinit()
if a.Sign() <= 0 || b.Sign() <= 0 {
z.SetInt64(0)
if x != nil {
x.SetInt64(0)
}
if y != nil {
y.SetInt64(0)
}
} else if x == nil && y == nil {
C.mpz_gcd(&z.i[0], &a.i[0], &b.i[0])
} else {
if x != nil {
x.doinit()
} else {
x = _Int0
}
if y != nil {
y.doinit()
} else {
y = _Int0
}
C.mpz_gcdext(&z.i[0], &x.i[0], &y.i[0], &a.i[0], &b.i[0])
}
return z
}
// ProbablyPrime performs n Miller-Rabin tests to check whether z is prime.
// If it returns true, z is prime with probability 1 - 1/4^n.
// If it returns false, z is not prime.
func (z *Int) ProbablyPrime(n int) bool {
z.doinit()
return int(C.mpz_probab_prime_p(&z.i[0], C.int(n))) > 0
}
// Rand sets z to a pseudo-random number in [0, n) and returns z.
func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int {
z.doinit()
// Get rid of n <= 0 case
if n.Sign() <= 0 {
z.SetInt64(0)
return z
}
// Make a copy of n if aliased
t := n
aliased := false
if n == z {
aliased = true
t = new(Int).Set(n)
}
// Work out bit sizes and masks
bits := n.BitLen() // >= 1
nwords := (bits + 31) / 32 // >= 1
bitLengthOfMSW := uint(bits % 32)
if bitLengthOfMSW == 0 {
bitLengthOfMSW = 32
}
mask := uint32((1 << bitLengthOfMSW) - 1)
words := make([]uint32, nwords)
for {
// Make a most significant first array of random bytes
for i := 0; i < nwords; i++ {
words[i] = rnd.Uint32()
}
// Mask out the top bits so this is only just bigger than n
words[0] &= mask
C.mpz_import(&z.i[0], C.size_t(len(words)), 1, 4, 0, 0, unsafe.Pointer(&words[0]))
// Exit if z < n - should take ~1.5 iterations of loop on average
if z.Cmp(t) < 0 {
break
}
}
if aliased {
t.Clear()
}
return z
}
// ModInverse sets z to the multiplicative inverse of g in the group /p (where
// p is a prime) and returns z.
func (z *Int) ModInverse(g, p *Int) *Int {
g.doinit()
p.doinit()
z.doinit()
C.mpz_invert(&z.i[0], &g.i[0], &p.i[0])
return z
}
// Lsh sets z = x << n and returns z.
func (z *Int) Lsh(x *Int, n uint) *Int {
x.doinit()
z.doinit()
C._mpz_mul_2exp(&z.i[0], &x.i[0], C.ulong(n))
return z
}
// Rsh sets z = x >> n and returns z.
func (z *Int) Rsh(x *Int, n uint) *Int {
x.doinit()
z.doinit()
C._mpz_div_2exp(&z.i[0], &x.i[0], C.ulong(n))
return z
}
// Bit returns the value of the i'th bit of z. That is, it
// returns (z>>i)&1. The bit index i must be >= 0.
func (z *Int) Bit(i int) uint {
z.doinit()
return uint(C._mpz_tstbit(&z.i[0], C.ulong(i)))
}
// SetBit sets z to x, with x's i'th bit set to b (0 or 1).
// That is, if b is 1 SetBit sets z = x | (1 << i);
// if b is 0 SetBit sets z = x &^ (1 << i). If b is not 0 or 1,
// SetBit will panic.
func (z *Int) SetBit(x *Int, i int, b uint) *Int {
if z != x {
z.Set(x)
}
if b == 0 {
C._mpz_clrbit(&z.i[0], C.ulong(i))
} else {
C._mpz_setbit(&z.i[0], C.ulong(i))
}
return z
}
// And sets z = x & y and returns z.
func (z *Int) And(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
C.mpz_and(&z.i[0], &x.i[0], &y.i[0])
return z
}
// AndNot sets z = x &^ y and returns z.
func (z *Int) AndNot(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
t := z
aliased := false
if z == y || z == x {
aliased = true
t = new(Int).Set(y)
}
C.mpz_com(&t.i[0], &y.i[0])
C.mpz_and(&z.i[0], &x.i[0], &t.i[0])
if aliased {
t.Clear()
}
return z
}
// Or sets z = x | y and returns z.
func (z *Int) Or(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
C.mpz_ior(&z.i[0], &x.i[0], &y.i[0])
return z
}
// Xor sets z = x ^ y and returns z.
func (z *Int) Xor(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
C.mpz_xor(&z.i[0], &x.i[0], &y.i[0])
return z
}
// Not sets z = ^x and returns z.
func (z *Int) Not(x *Int) *Int {
x.doinit()
z.doinit()
C.mpz_com(&z.i[0], &x.i[0])
return z
}
// Gob codec version. Permits backward-compatible changes to the encoding.
const intGobVersion byte = 1
// GobEncode implements the gob.GobEncoder interface.
func (z *Int) GobEncode() ([]byte, error) {
buf := make([]byte, 2+(z.BitLen()+7)/8)
n := C.size_t(len(buf) - 1)
C.mpz_export(unsafe.Pointer(&buf[1]), &n, 1, 1, 1, 0, &z.i[0])
b := intGobVersion << 1 // make space for sign bit
if z.Sign() < 0 {
b |= 1
}
buf[0] = b
return buf[:n+1], nil
}
// GobDecode implements the gob.GobDecoder interface.
func (z *Int) GobDecode(buf []byte) error {
if len(buf) == 0 {
return errors.New("Int.GobDecode: no data")
}
b := buf[0]
if b>>1 != intGobVersion {
return fmt.Errorf("Int.GobDecode: encoding version %d not supported", b>>1)
}
z.SetBytes(buf[1:])
if b&1 != 0 {
C.mpz_neg(&z.i[0], &z.i[0])
}
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (z *Int) MarshalJSON() ([]byte, error) {
// TODO(gri): get rid of the []byte/string conversions
return []byte(z.String()), nil
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (z *Int) UnmarshalJSON(x []byte) error {
// TODO(gri): get rid of the []byte/string conversions
_, ok := z.SetString(string(x), 0)
if !ok {
return fmt.Errorf("math/big: cannot unmarshal %s into a *gmp.Int", x)
}
return nil
}
+211
View File
@@ -0,0 +1,211 @@
// Copyright 2013 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.
// Implement extra functionality not in big.Int.
//
// Because the underlying C compiler may use 32-bit longs we
// use 32-bit integers here for maximum portability and speed.
//go:build gmp
// +build gmp
package gmp
/*
#cgo LDFLAGS: -lgmp
#include <gmp.h>
#include <stdlib.h>
*/
import "C"
// Sqrt sets x to the truncated integer part of the square root of x
//
// NB This is not part of big.Int
func (z *Int) Sqrt(x *Int) *Int {
x.doinit()
z.doinit()
C.mpz_sqrt(&z.i[0], &x.i[0])
return z
}
// Swap exchanges the values of z and x and returns the new z.
//
// NB This is not part of big.Int
func (z *Int) Swap(x *Int) *Int {
x.doinit()
z.doinit()
C.mpz_swap(&z.i[0], &x.i[0])
return z
}
// AddUint32 sets z to the sum x+y and returns z.
//
// NB This is not part of big.Int
func (z *Int) AddUint32(x *Int, y uint32) *Int {
x.doinit()
z.doinit()
C.mpz_add_ui(&z.i[0], &x.i[0], C.ulong(y))
return z
}
// SubUint32 sets z to the difference x-y and returns z.
//
// NB This is not part of big.Int
func (z *Int) SubUint32(x *Int, y uint32) *Int {
x.doinit()
z.doinit()
C.mpz_sub_ui(&z.i[0], &x.i[0], C.ulong(y))
return z
}
// Uint32Sub sets z to the difference x-y and returns z.
//
// NB This is not part of big.Int
func (z *Int) Uint32Sub(x uint32, y *Int) *Int {
y.doinit()
z.doinit()
C.mpz_ui_sub(&z.i[0], C.ulong(x), &y.i[0])
return z
}
// MulUint32 sets z to the product x*y and returns z.
//
// NB This is not part of big.Int
func (z *Int) MulUint32(x *Int, y uint32) *Int {
x.doinit()
z.doinit()
C.mpz_mul_ui(&z.i[0], &x.i[0], C.ulong(y))
return z
}
// MulInt32 sets z to the product x*y and returns z.
//
// NB This is not part of big.Int
func (z *Int) MulInt32(x *Int, y int32) *Int {
x.doinit()
z.doinit()
C.mpz_mul_si(&z.i[0], &x.i[0], C.long(y))
return z
}
// AddMul sets z to z + x*y and returns z.
//
// NB This is not part of big.Int
func (z *Int) AddMul(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
C.mpz_addmul(&z.i[0], &x.i[0], &y.i[0])
return z
}
// AddMulUint32 sets z to z + x*y and returns z.
//
// NB This is not part of big.Int
func (z *Int) AddMulUint32(x *Int, y uint32) *Int {
x.doinit()
z.doinit()
C.mpz_addmul_ui(&z.i[0], &x.i[0], C.ulong(y))
return z
}
// SubMul sets z to z - x*y and returns z.
//
// NB This is not part of big.Int
func (z *Int) SubMul(x, y *Int) *Int {
x.doinit()
y.doinit()
z.doinit()
C.mpz_submul(&z.i[0], &x.i[0], &y.i[0])
return z
}
// SubMulUint32 sets z to z - x*y and returns z.
//
// NB This is not part of big.Int
func (z *Int) SubMulUint32(x *Int, y uint32) *Int {
x.doinit()
z.doinit()
C.mpz_submul_ui(&z.i[0], &x.i[0], C.ulong(y))
return z
}
// compared reduces the result of a GMP comparison to one of {-1, 0, 1}.
func compared(i C.int) int {
if i > 0 {
return 1
} else if i < 0 {
return -1
} else {
return 0
}
}
// CmpUint32 compares z and x and returns:
//
// -1 if z < x
// 0 if z == x
// +1 if z > x
//
// NB This is not part of big.Int
func (z *Int) CmpUint32(x uint32) int {
z.doinit()
return compared(C._mpz_cmp_ui(&z.i[0], C.ulong(x)))
}
// CmpInt32 compares z and x and returns:
//
// -1 if z < x
// 0 if z == x
// +1 if z > x
//
// NB This is not part of big.Int
func (z *Int) CmpInt32(x int32) int {
z.doinit()
return compared(C._mpz_cmp_si(&z.i[0], C.long(x)))
}
// CmpAbs compares |z| and |x| and returns:
//
// -1 if |z| < |x|
// 0 if |z| == |x|
// +1 if |z| > |x|
//
// NB This is not part of big.Int
func (z *Int) CmpAbs(x *Int) int {
x.doinit()
z.doinit()
return compared(C.mpz_cmpabs(&z.i[0], &x.i[0]))
}
// CmpAbsUint32 compares |z| and |x| and returns:
//
// -1 if |z| < |x|
// 0 if |z| == |x|
// +1 if |z| > |x|
//
// NB This is not part of big.Int
func (z *Int) CmpAbsUint32(x uint32) int {
z.doinit()
return compared(C.mpz_cmpabs_ui(&z.i[0], C.ulong(x)))
}
// Uint32 returns the uint32 representation of z, if z fits into a uint32.
// If z is too big then the least significant bits that do fit are returned.
// The sign of z is ignored, only the absolute value is used.
//
// NB This is not part of big.Int
func (z *Int) Uint32() uint32 {
z.doinit()
return uint32(C.mpz_get_ui(&z.i[0]))
}
// Int32 returns the int32 representation of z, if z fits into a signed int32.
// If z is too big to fit in a int32 then the result is undefined.
//
// NB This is not part of big.Int
func (z *Int) Int32() int32 {
z.doinit()
return int32(C.mpz_get_si(&z.i[0]))
}
+258
View File
@@ -0,0 +1,258 @@
// 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.
//go:build gmp
// +build gmp
// Test extra functionality
package gmp
import (
"math/rand"
"testing"
"time"
)
type binaryFun func(a, b int64) bool
type ternaryFun func(a, b, c int64) bool
var random *rand.Rand
var perFuncTests []int
func init() {
random = rand.New(rand.NewSource(time.Now().UnixNano()))
perFuncTests = random.Perm(50)
}
func randoms(num int, bits int) <-chan int64 {
channel := make(chan int64)
go func(out chan<- int64) {
for _, i := range random.Perm(num) {
ran := random.Int63n(1<<uint(bits-1) - 1)
if i&1 == 1 {
ran = -ran
}
out <- ran
}
close(out)
}(channel)
return channel
}
func absi(i int64) int64 {
if i < 0 {
return -i
}
return i
}
func cmpi(a, b int64) int {
if a < b {
return -1
}
if a > b {
return 1
}
return 0
}
func testBinary(t *testing.T, name string, fun binaryFun, maxBits int) {
small := [...]int64{0, 1, -1, 2, -2, 3, -3}
for _, x := range small {
for _, y := range small {
if !fun(x, y) {
t.Errorf("%s failed for %v and %v", name, x, y)
}
}
}
for x := range randoms(10, maxBits) {
for y := range randoms(10, maxBits) {
if !fun(x, y) {
t.Errorf("%s failed for %v and %v", name, x, y)
}
}
}
}
func testTernary(t *testing.T, name string, fun ternaryFun) {
small := [...]int64{0, 1, -1, 2, -2, 3, -3}
for _, x := range small {
for _, y := range small {
for _, z := range small {
if !fun(x, y, z) {
t.Errorf("%s failed for %v, %v and %v", name, x, y, z)
}
}
}
}
num, bits := 6, 30
for x := range randoms(num, bits) {
for y := range randoms(num, bits) {
for z := range randoms(num, bits) {
if !fun(x, y, z) {
t.Errorf("%s failed for %v, %v and %v", name, x, y, z)
}
}
}
}
}
func TestSwap(t *testing.T) {
fun := func(i, j int64) bool {
a := NewInt(i)
b := NewInt(j)
a.Swap(b)
return i == b.Int64() && j == a.Int64()
}
testBinary(t, "Swap", fun, 64)
}
func TestAddMul(t *testing.T) {
fun := func(i, j, k int64) bool {
a := NewInt(i)
return a.AddMul(NewInt(j), NewInt(k)).Int64() == i+j*k
}
testTernary(t, "AddMul", fun)
}
func TestAddUint32(t *testing.T) {
fun := func(i, j int64) bool {
a := NewInt(i)
k := uint32(j)
return a.AddUint32(a, k).Int64() == i+int64(k)
}
testBinary(t, "AddUint32", fun, 62)
}
func TestSubUint32(t *testing.T) {
fun := func(i, j int64) bool {
a := NewInt(i)
k := uint32(j)
return a.SubUint32(a, k).Int64() == i-int64(k)
}
testBinary(t, "SubUint32", fun, 62)
}
func TestUint32Sub(t *testing.T) {
fun := func(i, j int64) bool {
a := NewInt(i)
k := uint32(j)
return a.Uint32Sub(k, a).Int64() == int64(k)-i
}
testBinary(t, "Uint32Sub", fun, 62)
}
func TestMulUint32(t *testing.T) {
fun := func(i, j int64) bool {
a := NewInt(i)
k := uint32(j)
return a.MulUint32(a, k).Int64() == i*int64(k)
}
testBinary(t, "MulUint32", fun, 30)
}
func TestMulInt32(t *testing.T) {
fun := func(i, j int64) bool {
a := NewInt(i)
k := int32(j)
return a.MulInt32(a, k).Int64() == i*int64(k)
}
testBinary(t, "MulInt32", fun, 30)
}
func TestAddMulUint32(t *testing.T) {
fun := func(i, j, k int64) bool {
a := NewInt(i)
n := uint32(k)
return a.AddMulUint32(NewInt(j), n).Int64() == i+j*int64(n)
}
testTernary(t, "AddMulUint32", fun)
}
func TestSubMul(t *testing.T) {
fun := func(i, j, k int64) bool {
a := NewInt(i)
return a.SubMul(NewInt(j), NewInt(k)).Int64() == i-j*k
}
testTernary(t, "SubMul", fun)
}
func TestSubMulUint32(t *testing.T) {
fun := func(i, j, k int64) bool {
a := NewInt(i)
n := uint32(k)
return a.SubMulUint32(NewInt(j), n).Int64() == i-j*int64(n)
}
testTernary(t, "SubMulUint32", fun)
}
func TestCmpUint32(t *testing.T) {
fun := func(i, j int64) bool {
a := NewInt(i)
k := uint32(j)
return a.CmpUint32(k) == cmpi(i, int64(k))
}
testBinary(t, "CmpUint32", fun, 64)
}
func TestCmpInt32(t *testing.T) {
fun := func(i, j int64) bool {
a := NewInt(i)
k := int32(j)
return a.CmpInt32(k) == cmpi(i, int64(k))
}
testBinary(t, "CmpInt32", fun, 64)
}
func TestCmpAbs(t *testing.T) {
fun := func(i, j int64) bool {
a := NewInt(i)
return a.CmpAbs(NewInt(j)) == cmpi(absi(i), absi(j))
}
testBinary(t, "CmpAbs", fun, 64)
}
func TestCmpAbsUint32(t *testing.T) {
fun := func(i, j int64) bool {
a := NewInt(i)
k := uint32(j)
return a.CmpAbsUint32(k) == cmpi(absi(i), int64(k))
}
testBinary(t, "CmpAbsUint32", fun, 64)
}
func TestUint32(t *testing.T) {
for range perFuncTests {
var n = uint32(random.Int63())
if NewInt(int64(n)).Uint32() != n {
t.Errorf("Uint32 failed for %v", n)
}
}
}
func TestInt32(t *testing.T) {
for range perFuncTests {
var n = int32(random.Int63())
if NewInt(int64(n)).Int32() != n {
t.Errorf("Int32 failed for %v", n)
}
}
}
func TestSqrt(t *testing.T) {
a := NewInt(1)
aSquared := NewInt(1)
ten := NewInt(10)
hundred := NewInt(100)
root := new(Int)
for range perFuncTests {
root := root.Sqrt(aSquared)
if root.Cmp(a) != 0 {
t.Errorf("Sqrt failed got %d expecting %d", root, a)
}
a.Mul(a, ten)
aSquared.Mul(aSquared, hundred)
}
}
+78
View File
@@ -0,0 +1,78 @@
//go:build gmp
// +build gmp
package gmp
/*
#cgo LDFLAGS: -lgmp
#include <gmp.h>
#include <stdlib.h>
*/
import "C"
import (
"math/big"
)
const MaxBase = 62
// itoa is like utoa but it prepends a '-' if neg && x != 0.
func itoa(x []byte, neg bool, base int) string {
if base < 2 || base > MaxBase {
panic("invalid base")
}
// x == 0
if len(x) == 0 {
return "0"
}
// len(x) > 0
n := new(big.Int).SetBytes(x)
if neg {
n.Neg(n)
}
return n.Text(base)
}
// Text returns the string representation of x in the given base.
// Base must be between 2 and 62, inclusive. The result uses the
// lower-case letters 'a' to 'z' for digit values 10 to 35, and
// the upper-case letters 'A' to 'Z' for digit values 36 to 61.
// No prefix (such as "0x") is added to the string. If x is a nil
// pointer it returns "<nil>".
func (x *Int) Text(base int) string {
if x == nil {
return "<nil>"
}
nat := x.Bytes()
return string(itoa(nat, x.Sign() < 0, base))
}
// Bits return the bits of x
func (x *Int) Bits() uint {
// return uint(C.mpz_bitcnt(x.i))
panic("unimplement")
}
// ToNBytes ouput a bytes to fix n bytes. extend as 0.
func toNBytes(b *Int, n int) []byte {
abs := b.Bytes()
l := len(abs)
var s []byte
// l==n is the most case (255/256)
if l >= n {
s = abs[l-n : l]
} else {
s = make([]byte, n)
copy(s[n-l:], abs)
}
return s
}
func (x *Int) FillBytes(b []byte) []byte {
bb := toNBytes(x, len(b))
copy(b, bb)
return b
}
File diff suppressed because it is too large Load Diff
+650
View File
@@ -0,0 +1,650 @@
// 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.
//go:build gmp
// +build gmp
package gmp
/*
#include <gmp.h>
#include <stdlib.h>
// Macros
int __mpq_sgn(mpq_ptr op) {
return mpq_sgn(op);
}
int __mpz_cmp_ui(mpz_ptr op, unsigned long n) {
return mpz_cmp_ui(op, n);
}
mpz_ptr _mpq_numref(mpq_t op) {
return mpq_numref(op);
}
mpz_ptr _mpq_denref(mpq_t op) {
return mpq_denref(op);
}
// Sign of the numerator
int _mpq_num_sgn(mpq_t op) {
return mpz_sgn(mpq_numref(op));
}
*/
import "C"
import (
"encoding/binary"
"errors"
"fmt"
"math"
"runtime"
"strings"
"unsafe"
)
// A Rat represents a quotient a/b of arbitrary precision.
// The zero value for a Rat represents the value 0.
type Rat struct {
i C.mpq_t
init bool
}
// Finalizer - release the memory allocated to the mpz
func ratFinalize(z *Rat) {
if z.init {
runtime.SetFinalizer(z, nil)
C.mpq_clear(&z.i[0])
z.init = false
}
}
// Rat promises that the zero value is a 0, but in gmp
// the zero value is a crash. To bridge the gap, the
// init bool says whether this is a valid gmp value.
// doinit initializes z.i if it needs it.
func (z *Rat) doinit() {
if z.init {
return
}
z.init = true
C.mpq_init(&z.i[0])
runtime.SetFinalizer(z, ratFinalize)
}
// Clear the allocated space used by the number
//
// This normally happens on a runtime.SetFinalizer call, but if you
// want immediate deallocation you can call it.
//
// NB This is not part of big.Rat
func (z *Rat) Clear() {
ratFinalize(z)
}
// NewRat creates a new Rat with numerator a and denominator b.
func NewRat(a, b int64) *Rat {
return new(Rat).SetFrac64(a, b)
}
// SetFloat64 sets z to exactly f and returns z.
// If f is not finite, SetFloat returns nil.
func (z *Rat) SetFloat64(f float64) *Rat {
if math.IsNaN(f) || math.IsInf(f, 0) {
return nil
}
z.doinit()
C.mpq_set_d(&z.i[0], C.double(f))
return z
}
// Float64Gmp returns the nearest float64 value for z and a bool indicating
// whether f represents z exactly. If the magnitude of z is too large to
// be represented by a float64, f is an infinity and exact is false.
// The sign of f always matches the sign of z, even if f == 0.
//
// NB This uses GMP which is fast but rounds differently to Float64
func (z *Rat) Float64Gmp() (f float64, exact bool) {
z.doinit()
f = float64(C.mpq_get_d(&z.i[0]))
if !(math.IsNaN(f) || math.IsInf(f, 0)) {
exact = new(Rat).SetFloat64(f).Cmp(z) == 0
}
return
}
// low64 returns the least significant 64 bits of natural number z.
func low64(z *Int) uint64 {
// FIXME not wildy efficient!
t := new(Int).SetUint64(0xffffffffffffffff)
t.And(t, z)
return t.Uint64()
}
// quotToFloat returns the non-negative IEEE 754 double-precision
// value nearest to the quotient a/b, using round-to-even in halfway
// cases. It does not mutate its arguments.
// Preconditions: b is non-zero; a and b have no common factors.
func quotToFloat(a, b *Int) (f float64, exact bool) {
// TODO(adonovan): specialize common degenerate cases: 1.0, integers.
alen := a.BitLen()
if alen == 0 {
return 0, true
}
blen := b.BitLen()
if blen == 0 {
panic("division by zero")
}
// 1. Left-shift A or B such that quotient A/B is in [1<<53, 1<<55).
// (54 bits if A<B when they are left-aligned, 55 bits if A>=B.)
// This is 2 or 3 more than the float64 mantissa field width of 52:
// - the optional extra bit is shifted away in step 3 below.
// - the high-order 1 is omitted in float64 "normal" representation;
// - the low-order 1 will be used during rounding then discarded.
exp := alen - blen
a2, b2 := new(Int).Set(a), new(Int).Set(b)
if shift := 54 - exp; shift > 0 {
a2.Lsh(a2, uint(shift))
} else if shift < 0 {
b2.Lsh(b2, uint(-shift))
}
// 2. Compute quotient and remainder (q, r). NB: due to the
// extra shift, the low-order bit of q is logically the
// high-order bit of r.
q, r := new(Int).DivMod(a2, b2, new(Int)) // (recycle a2)
mantissa := low64(q)
haveRem := r.Sign() != 0 // mantissa&1 && !haveRem => remainder is exactly half
// 3. If quotient didn't fit in 54 bits, re-do division by b2<<1
// (in effect---we accomplish this incrementally).
if mantissa>>54 == 1 {
if mantissa&1 == 1 {
haveRem = true
}
mantissa >>= 1
exp++
}
if mantissa>>53 != 1 {
panic("expected exactly 54 bits of result")
}
// 4. Rounding.
if -1022-52 <= exp && exp <= -1022 {
// Denormal case; lose 'shift' bits of precision.
shift := uint64(-1022 - (exp - 1)) // [1..53)
lostbits := mantissa & (1<<shift - 1)
haveRem = haveRem || lostbits != 0
mantissa >>= shift
exp = -1023 + 2
}
// Round q using round-half-to-even.
exact = !haveRem
if mantissa&1 != 0 {
exact = false
if haveRem || mantissa&2 != 0 {
if mantissa++; mantissa >= 1<<54 {
// Complete rollover 11...1 => 100...0, so shift is safe
mantissa >>= 1
exp++
}
}
}
mantissa >>= 1 // discard rounding bit. Mantissa now scaled by 2^53.
f = math.Ldexp(float64(mantissa), exp-53)
if math.IsInf(f, 0) {
exact = false
}
return
}
// Float64 returns the nearest float64 value for z and a bool indicating
// whether f represents z exactly. If the magnitude of z is too large to
// be represented by a float64, f is an infinity and exact is false.
// The sign of f always matches the sign of z, even if f == 0.
func (z *Rat) Float64() (f float64, exact bool) {
a := z.Num()
negative := false
if a.Sign() < 0 {
a.Neg(a)
negative = true
}
b := z.Denom()
f, exact = quotToFloat(a, b)
if negative {
f = -f
}
return
}
// SetNum sets the numerator of z returning z
//
// NB this isn't part of math/big which uses Num().Set() for this
// purpose. In gmp Num() returns a copy hence the need for a SetNum()
// method.
func (z *Rat) SetNum(a *Int) *Rat {
z.doinit()
a.doinit()
C.mpq_set_num(&z.i[0], &a.i[0])
C.mpq_canonicalize(&z.i[0])
return z
}
// SetDenom sets the numerator of z returning z
//
// NB this isn't part of math/big which uses Num().Set() for this
// purpose. In gmp Num() returns a copy hence the need for a SetNum()
// method.
func (z *Rat) SetDenom(a *Int) *Rat {
z.doinit()
a.doinit()
C.mpq_set_den(&z.i[0], &a.i[0])
// If numerator is zero don't canonicalize
if C._mpq_num_sgn(&z.i[0]) != 0 {
C.mpq_canonicalize(&z.i[0])
}
return z
}
// SetFrac sets z to a/b and returns z.
func (z *Rat) SetFrac(a, b *Int) *Rat {
z.doinit()
a.doinit()
b.doinit()
// FIXME copying? or referencing?
C.mpq_set_num(&z.i[0], &a.i[0])
C.mpq_set_den(&z.i[0], &b.i[0])
C.mpq_canonicalize(&z.i[0])
return z
}
// SetFrac64 sets z to a/b and returns z.
func (z *Rat) SetFrac64(a, b int64) *Rat {
z.doinit()
if b == 0 {
panic("division by zero")
}
// Detect overflow if running on 32 bits
if a == int64(C.long(a)) && b == int64(C.long(b)) {
if b < 0 {
a = -a
b = -b
}
C.mpq_set_si(&z.i[0], C.long(a), C.ulong(b))
C.mpq_canonicalize(&z.i[0])
if b < 0 {
// This only happens when b = 1<<63
z.Neg(z)
}
} else {
// Slow path but will work on 32 bit architectures
z.SetFrac(NewInt(a), NewInt(b))
}
return z
}
// SetInt sets z to x (by making a copy of x) and returns z.
func (z *Rat) SetInt(x *Int) *Rat {
z.doinit()
// FIXME copying? or referencing?
C.mpq_set_z(&z.i[0], &x.i[0])
return z
}
// SetInt64 sets z to x and returns z.
func (z *Rat) SetInt64(x int64) *Rat {
z.SetFrac64(x, 1)
return z
}
// Set sets z to x (by making a copy of x) and returns z.
func (z *Rat) Set(x *Rat) *Rat {
if z != x {
z.doinit()
C.mpq_set(&z.i[0], &x.i[0])
}
return z
}
// Abs sets z to |x| (the absolute value of x) and returns z.
func (z *Rat) Abs(x *Rat) *Rat {
z.doinit()
C.mpq_abs(&z.i[0], &x.i[0])
return z
}
// Neg sets z to -x and returns z.
func (z *Rat) Neg(x *Rat) *Rat {
z.doinit()
C.mpq_neg(&z.i[0], &x.i[0])
return z
}
// Inv sets z to 1/x and returns z.
func (z *Rat) Inv(x *Rat) *Rat {
z.doinit()
x.doinit()
if x.Sign() == 0 {
panic("division by zero")
}
C.mpq_inv(&z.i[0], &x.i[0])
return z
}
// Sign returns:
//
// -1 if z < 0
// 0 if z == 0
// +1 if z > 0
//
func (z *Rat) Sign() int {
z.doinit()
return int(C.__mpq_sgn(&z.i[0]))
}
// IsInt returns true if the denominator of z is 1.
func (z *Rat) IsInt() bool {
z.doinit()
return C.__mpz_cmp_ui(C._mpq_denref(&z.i[0]), C.ulong(1)) == 0
}
// Num returns the numerator of z; it may be <= 0. The result is a
// copy of z's numerator; it won't change if a new value is assigned
// to z, and vice versa. The sign of the numerator corresponds to the
// sign of z.
//
// NB In math/big this is a reference to the numerator not a copy
func (z *Rat) Num() *Int {
// Return an initialised *Int so we don't initialize or finalize it by accident
z.doinit()
res := new(Int)
res.doinit()
C.mpq_get_num(&res.i[0], &z.i[0])
return res
}
// Denom returns the denominator of z; it is always > 0. The result
// is a copy of z's denominator; it won't change if a new value is
// assigned to z, and vice versa.
//
// NB In math/big this is a reference to the denominator not a copy
func (z *Rat) Denom() *Int {
// Return an initialised *Int so we don't initialize or finalize it by accident
z.doinit()
res := new(Int)
res.doinit()
C.mpq_get_den(&res.i[0], &z.i[0])
return res
}
// Cmp compares z and y and returns:
//
// -1 if z < y
// 0 if z == y
// +1 if z > y
//
func (z *Rat) Cmp(y *Rat) (r int) {
z.doinit()
y.doinit()
r = int(C.mpq_cmp(&z.i[0], &y.i[0]))
if r < 0 {
r = -1
} else if r > 0 {
r = 1
}
return
}
// Add sets z to the sum x+y and returns z.
func (z *Rat) Add(x, y *Rat) *Rat {
x.doinit()
y.doinit()
z.doinit()
C.mpq_add(&z.i[0], &x.i[0], &y.i[0])
return z
}
// Sub sets z to the difference x-y and returns z.
func (z *Rat) Sub(x, y *Rat) *Rat {
x.doinit()
y.doinit()
z.doinit()
C.mpq_sub(&z.i[0], &x.i[0], &y.i[0])
return z
}
// Mul sets z to the product x*y and returns z.
func (z *Rat) Mul(x, y *Rat) *Rat {
x.doinit()
y.doinit()
z.doinit()
C.mpq_mul(&z.i[0], &x.i[0], &y.i[0])
return z
}
// 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 {
x.doinit()
y.doinit()
z.doinit()
if y.Sign() == 0 {
panic("division by zero")
}
C.mpq_div(&z.i[0], &x.i[0], &y.i[0])
return z
}
func ratTok(ch rune) bool {
return strings.IndexRune("+-/0123456789.eE", ch) >= 0
}
// Scan is a support routine for fmt.Scanner. It accepts the formats
// 'e', 'E', 'f', 'F', 'g', 'G', and 'v'. All formats are equivalent.
func (z *Rat) Scan(s fmt.ScanState, ch rune) error {
tok, err := s.Token(true, ratTok)
if err != nil {
return err
}
if strings.IndexRune("efgEFGv", ch) < 0 {
return errors.New("Rat.Scan: invalid verb")
}
if _, ok := z.SetString(string(tok)); !ok {
return errors.New("Rat.Scan: invalid syntax")
}
return nil
}
// 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 floating-point number
// optionally followed by an exponent. If the operation failed, the value of
// z is undefined but the returned value is nil.
func (z *Rat) SetString(s string) (*Rat, bool) {
if len(s) == 0 {
return nil, false
}
z.doinit()
a := new(Int)
b := new(Int)
// check for a quotient
sep := strings.Index(s, "/")
if sep >= 0 {
// FIXME Num and Denom are bust
// if _, ok := z.Num().SetString(s[0:sep], 10); !ok {
// return nil, false
// }
// if _, ok := z.Denom().SetString(s[sep+1:], 10); !ok {
// return nil, false
// }
if _, ok := a.SetString(s[0:sep], 10); !ok {
return nil, false
}
if _, ok := b.SetString(s[sep+1:], 10); !ok {
return nil, false
}
z.SetFrac(a, b)
C.mpq_canonicalize(&z.i[0])
return z, true
}
// check for a decimal point
sep = strings.Index(s, ".")
// check for an exponent
e := strings.IndexAny(s, "eE")
exp := new(Int)
if e >= 0 {
if e < sep {
// The E must come after the decimal point.
return nil, false
}
if _, ok := exp.SetString(s[e+1:], 10); !ok {
return nil, false
}
s = s[0:e]
}
if sep >= 0 {
s = s[0:sep] + s[sep+1:]
exp.Sub(exp, NewInt(int64(len(s)-sep)))
}
if _, ok := a.SetString(s, 10); !ok {
return nil, false
}
absExp := new(Int).Abs(exp)
powTen := new(Int).Exp(_Int10, absExp, nil)
if exp.Sign() < 0 {
b = powTen
} else {
a.Mul(a, powTen)
b.SetInt64(1)
}
z.SetFrac(a, b)
C.mpq_canonicalize(&z.i[0])
return z, true
}
// string returns z in the base given
func (z *Rat) string(base int) string {
if z == nil {
return "<nil>"
}
z.doinit()
p := C.mpq_get_str(nil, C.int(base), &z.i[0])
s := C.GoString(p)
C.free(unsafe.Pointer(p))
return s
}
// String returns a string representation of z in the form "a/b" (even if b == 1).
func (z *Rat) String() string {
s := z.string(10)
if !strings.Contains(s, "/") {
s += "/1"
}
return s
}
// RatString returns a string representation of z in the form "a/b" if b != 1,
// and in the form "a" if b == 1.
func (z *Rat) RatString() string {
return z.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 {
if z.IsInt() {
s := z.string(10)
if prec > 0 {
s += "." + strings.Repeat("0", prec)
}
return s
}
a := z.Num()
a.Abs(a)
b := z.Denom()
q, r := new(Int).DivMod(a, b, new(Int))
p := _Int1
if prec > 0 {
p = new(Int).Exp(_Int10, NewInt(int64(prec)), nil)
}
r.Mul(r, p)
r2 := new(Int)
r.DivMod(r, b, r2)
// see if we need to round up
r2.Add(r2, r2)
if b.Cmp(r2) <= 0 {
r.Add(r, _Int1)
if r.Cmp(p) >= 0 {
q.Add(q, _Int1)
r.Sub(r, p)
}
}
s := q.string(10)
if z.Sign() < 0 {
s = "-" + s
}
if prec > 0 {
rs := r.string(10)
leadingZeros := prec - len(rs)
s += "." + strings.Repeat("0", leadingZeros) + rs
}
return s
}
// Gob codec version. Permits backward-compatible changes to the encoding.
const ratGobVersion byte = 1
// GobEncode implements the gob.GobEncoder interface.
func (z *Rat) GobEncode() ([]byte, error) {
bufa := z.Num().Bytes()
bufb := z.Denom().Bytes()
buf := make([]byte, 1+4) // extra bytes for version and sign bit (1), and numerator length (4)
buf = append(buf, bufa...)
buf = append(buf, bufb...)
const j = 1 + 4
n := len(bufa)
if int(uint32(n)) != n {
// this should never happen
return nil, errors.New("Rat.GobEncode: numerator too large")
}
binary.BigEndian.PutUint32(buf[1:5], uint32(n))
b := ratGobVersion << 1 // make space for sign bit
if z.Sign() < 0 {
b |= 1
}
buf[0] = b
return buf, nil
}
// GobDecode implements the gob.GobDecoder interface.
func (z *Rat) GobDecode(buf []byte) error {
if len(buf) == 0 {
return errors.New("Rat.GobDecode: no data")
}
b := buf[0]
if b>>1 != ratGobVersion {
return fmt.Errorf("Rat.GobDecode: encoding version %d not supported", b>>1)
}
const j = 1 + 4
i := j + binary.BigEndian.Uint32(buf[j-4:j])
num := new(Int).SetBytes(buf[j:i])
den := new(Int).SetBytes(buf[i:])
if b&1 != 0 {
num.Neg(num)
}
z.SetFrac(num, den)
return nil
}
+910
View File
@@ -0,0 +1,910 @@
// 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.
//go:build gmp
// +build gmp
package gmp
import (
"bytes"
"encoding/gob"
"fmt"
"math"
"strconv"
"strings"
"testing"
)
func TestZeroRat(t *testing.T) {
var x, y, z Rat
y.SetFrac64(0, 42)
if x.Cmp(&y) != 0 {
t.Errorf("x and y should be both equal and zero")
}
if s := x.String(); s != "0/1" {
t.Errorf("got x = %s, want 0/1", s)
}
if s := x.RatString(); s != "0" {
t.Errorf("got x = %s, want 0", s)
}
z.Add(&x, &y)
if s := z.RatString(); s != "0" {
t.Errorf("got x+y = %s, want 0", s)
}
z.Sub(&x, &y)
if s := z.RatString(); s != "0" {
t.Errorf("got x-y = %s, want 0", s)
}
z.Mul(&x, &y)
if s := z.RatString(); s != "0" {
t.Errorf("got x*y = %s, want 0", s)
}
// check for division by zero
defer func() {
if s := recover(); s == nil || s.(string) != "division by zero" {
panic(s)
}
}()
z.Quo(&x, &y)
}
var setStringTests = []struct {
in, out string
ok bool
}{
{"0", "0", true},
{"-0", "0", true},
{"1", "1", true},
{"-1", "-1", true},
{"1.", "1", true},
{"1e0", "1", true},
{"1.e1", "10", true},
{in: "1e", ok: false},
{in: "1.e", ok: false},
{in: "1e+14e-5", ok: false},
{in: "1e4.5", ok: false},
{in: "r", ok: false},
{in: "a/b", ok: false},
{in: "a.b", ok: false},
{"-0.1", "-1/10", true},
{"-.1", "-1/10", true},
{"2/4", "1/2", true},
{".25", "1/4", true},
{"-1/5", "-1/5", true},
{"8129567.7690E14", "812956776900000000000", true},
{"78189e+4", "781890000", true},
{"553019.8935e+8", "55301989350000", true},
{"98765432109876543210987654321e-10", "98765432109876543210987654321/10000000000", true},
{"9877861857500000E-7", "3951144743/4", true},
{"2169378.417e-3", "2169378417/1000000", true},
{"884243222337379604041632732738665534", "884243222337379604041632732738665534", true},
{"53/70893980658822810696", "53/70893980658822810696", true},
{"106/141787961317645621392", "53/70893980658822810696", true},
{"204211327800791583.81095", "4084226556015831676219/20000", true},
}
func TestRatSetString(t *testing.T) {
for i, test := range setStringTests {
x, ok := new(Rat).SetString(test.in)
if ok {
if !test.ok {
t.Errorf("#%d SetString(%q) expected failure", i, test.in)
} else if x.RatString() != test.out {
t.Errorf("#%d SetString(%q) got %s want %s", i, test.in, x.RatString(), test.out)
}
} else if x != nil {
t.Errorf("#%d SetString(%q) got %p want nil", i, test.in, x)
}
}
}
func TestRatScan(t *testing.T) {
var buf bytes.Buffer
for i, test := range setStringTests {
x := new(Rat)
buf.Reset()
_, _ = buf.WriteString(test.in)
_, err := fmt.Fscanf(&buf, "%v", x)
if err == nil != test.ok {
if test.ok {
t.Errorf("#%d error: %s", i, err)
} else {
t.Errorf("#%d expected error", i)
}
continue
}
if err == nil && x.RatString() != test.out {
t.Errorf("#%d got %s want %s", i, x.RatString(), test.out)
}
}
}
var floatStringTests = []struct {
in string
prec int
out string
}{
{"0", 0, "0"},
{"0", 4, "0.0000"},
{"1", 0, "1"},
{"1", 2, "1.00"},
{"-1", 0, "-1"},
{".25", 2, "0.25"},
{".25", 1, "0.3"},
{".25", 3, "0.250"},
{"-1/3", 3, "-0.333"},
{"-2/3", 4, "-0.6667"},
{"0.96", 1, "1.0"},
{"0.999", 2, "1.00"},
{"0.9", 0, "1"},
{".25", -1, "0"},
{".55", -1, "1"},
}
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)
}
}
}
func TestRatSign(t *testing.T) {
zero := NewRat(0, 1)
for _, a := range setStringTests {
x, ok := new(Rat).SetString(a.in)
if !ok {
continue
}
s := x.Sign()
e := x.Cmp(zero)
if s != e {
t.Errorf("got %d; want %d for z = %v", s, e, &x)
}
}
}
var ratCmpTests = []struct {
rat1, rat2 string
out int
}{
{"0", "0/1", 0},
{"1/1", "1", 0},
{"-1", "-2/2", 0},
{"1", "0", 1},
{"0/1", "1/1", -1},
{"-5/1434770811533343057144", "-5/1434770811533343057145", -1},
{"49832350382626108453/8964749413", "49832350382626108454/8964749413", -1},
{"-37414950961700930/7204075375675961", "37414950961700930/7204075375675961", -1},
{"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)
}
}
}
func TestIsInt(t *testing.T) {
one := NewInt(1)
for _, a := range setStringTests {
x, ok := new(Rat).SetString(a.in)
if !ok {
continue
}
i := x.IsInt()
e := x.Denom().Cmp(one) == 0
if i != e {
t.Errorf("got IsInt(%v) == %v; want %v", x, i, e)
}
}
}
func TestRatAbs(t *testing.T) {
zero := new(Rat)
for _, a := range setStringTests {
x, ok := new(Rat).SetString(a.in)
if !ok {
continue
}
e := new(Rat).Set(x)
if e.Cmp(zero) < 0 {
e.Sub(zero, e)
}
z := new(Rat).Abs(x)
if z.Cmp(e) != 0 {
t.Errorf("got Abs(%v) = %v; want %v", x, z, e)
}
}
}
func TestRatNeg(t *testing.T) {
zero := new(Rat)
for _, a := range setStringTests {
x, ok := new(Rat).SetString(a.in)
if !ok {
continue
}
e := new(Rat).Sub(zero, x)
z := new(Rat).Neg(x)
if z.Cmp(e) != 0 {
t.Errorf("got Neg(%v) = %v; want %v", x, z, e)
}
}
}
func TestRatInv(t *testing.T) {
zero := new(Rat)
for _, a := range setStringTests {
x, ok := new(Rat).SetString(a.in)
if !ok {
continue
}
if x.Cmp(zero) == 0 {
continue // avoid division by zero
}
e := new(Rat).SetFrac(x.Denom(), x.Num())
z := new(Rat).Inv(x)
if z.Cmp(e) != 0 {
t.Errorf("got Inv(%v) = %v; want %v", x, z, e)
}
}
}
type ratBinFun func(z, x, y *Rat) *Rat
type ratBinArg struct {
x, y, z string
}
func testRatBin(t *testing.T, i int, name string, f ratBinFun, a ratBinArg) {
x, _ := new(Rat).SetString(a.x)
y, _ := new(Rat).SetString(a.y)
z, _ := new(Rat).SetString(a.z)
out := f(new(Rat), x, y)
if out.Cmp(z) != 0 {
t.Errorf("%s #%d got %s want %s", name, i, out, z)
}
}
var ratBinTests = []struct {
x, y string
sum, prod string
}{
{"0", "0", "0", "0"},
{"0", "1", "1", "0"},
{"-1", "0", "-1", "0"},
{"-1", "1", "0", "-1"},
{"1", "1", "2", "1"},
{"1/2", "1/2", "1", "1/4"},
{"1/4", "1/3", "7/12", "1/12"},
{"2/5", "-14/3", "-64/15", "-28/15"},
{"4707/49292519774798173060", "-3367/70976135186689855734", "84058377121001851123459/1749296273614329067191168098769082663020", "-1760941/388732505247628681598037355282018369560"},
{"-61204110018146728334/3", "-31052192278051565633/2", "-215564796870448153567/6", "950260896245257153059642991192710872711/3"},
{"-854857841473707320655/4237645934602118692642972629634714039", "-18/31750379913563777419", "-27/133467566250814981", "15387441146526731771790/134546868362786310073779084329032722548987800600710485341"},
{"618575745270541348005638912139/19198433543745179392300736", "-19948846211000086/637313996471", "27674141753240653/30123979153216", "-6169936206128396568797607742807090270137721977/6117715203873571641674006593837351328"},
{"-3/26206484091896184128", "5/2848423294177090248", "15310893822118706237/9330894968229805033368778458685147968", "-5/24882386581946146755650075889827061248"},
{"26946729/330400702820", "41563965/225583428284", "1238218672302860271/4658307703098666660055", "224002580204097/14906584649915733312176"},
{"-8259900599013409474/7", "-84829337473700364773/56707961321161574960", "-468402123685491748914621885145127724451/396955729248131024720", "350340947706464153265156004876107029701/198477864624065512360"},
{"575775209696864/1320203974639986246357", "29/712593081308", "410331716733912717985762465/940768218243776489278275419794956", "808/45524274987585732633"},
{"1786597389946320496771/2066653520653241", "6269770/1992362624741777", "3559549865190272133656109052308126637/4117523232840525481453983149257", "8967230/3296219033"},
{"-36459180403360509753/32150500941194292113930", "9381566963714/9633539", "301622077145533298008420642898530153/309723104686531919656937098270", "-3784609207827/3426986245"},
}
func TestRatBin(t *testing.T) {
for i, test := range ratBinTests {
arg := ratBinArg{test.x, test.y, test.sum}
testRatBin(t, i, "Add", (*Rat).Add, arg)
arg = ratBinArg{test.y, test.x, test.sum}
testRatBin(t, i, "Add symmetric", (*Rat).Add, arg)
arg = ratBinArg{test.sum, test.x, test.y}
testRatBin(t, i, "Sub", (*Rat).Sub, arg)
arg = ratBinArg{test.sum, test.y, test.x}
testRatBin(t, i, "Sub symmetric", (*Rat).Sub, arg)
arg = ratBinArg{test.x, test.y, test.prod}
testRatBin(t, i, "Mul", (*Rat).Mul, arg)
arg = ratBinArg{test.y, test.x, test.prod}
testRatBin(t, i, "Mul symmetric", (*Rat).Mul, arg)
if test.x != "0" {
arg = ratBinArg{test.prod, test.x, test.y}
testRatBin(t, i, "Quo", (*Rat).Quo, arg)
}
if test.y != "0" {
arg = ratBinArg{test.prod, test.y, test.x}
testRatBin(t, i, "Quo symmetric", (*Rat).Quo, arg)
}
}
}
func TestIssue820(t *testing.T) {
x := NewRat(3, 1)
y := NewRat(2, 1)
z := y.Quo(x, y)
q := NewRat(3, 2)
if z.Cmp(q) != 0 {
t.Errorf("got %s want %s", z, q)
}
y = NewRat(3, 1)
x = NewRat(2, 1)
z = y.Quo(x, y)
q = NewRat(2, 3)
if z.Cmp(q) != 0 {
t.Errorf("got %s want %s", z, q)
}
x = NewRat(3, 1)
z = x.Quo(x, x)
q = NewRat(3, 3)
if z.Cmp(q) != 0 {
t.Errorf("got %s want %s", z, q)
}
}
var setFrac64Tests = []struct {
a, b int64
out string
}{
{0, 1, "0"},
{0, -1, "0"},
{1, 1, "1"},
{-1, 1, "-1"},
{1, -1, "-1"},
{-1, -1, "1"},
{-9223372036854775808, -9223372036854775808, "1"},
}
func TestRatSetFrac64Rat(t *testing.T) {
for i, test := range setFrac64Tests {
x := new(Rat).SetFrac64(test.a, test.b)
if x.RatString() != test.out {
t.Errorf("#%d got %s want %s", i, x.RatString(), test.out)
}
}
}
func TestRatGobEncoding(t *testing.T) {
var medium bytes.Buffer
enc := gob.NewEncoder(&medium)
dec := gob.NewDecoder(&medium)
for _, test := range encodingTests {
medium.Reset() // empty buffer for each test case (in case of failures)
var tx Rat
tx.SetString(test + ".14159265")
if err := enc.Encode(&tx); err != nil {
t.Errorf("encoding of %s failed: %s", &tx, err)
}
var rx Rat
if err := dec.Decode(&rx); err != nil {
t.Errorf("decoding of %s failed: %s", &tx, err)
}
if rx.Cmp(&tx) != 0 {
t.Errorf("transmission of %s failed: got %s want %s", &tx, &rx, &tx)
}
}
}
func TestIssue2379(t *testing.T) {
// 1) no aliasing
q := NewRat(3, 2)
x := new(Rat)
x.SetFrac(NewInt(3), NewInt(2))
if x.Cmp(q) != 0 {
t.Errorf("1) got %s want %s", x, q)
}
// 2) aliasing of numerator
x = NewRat(2, 3)
x.SetFrac(NewInt(3), x.Num())
if x.Cmp(q) != 0 {
t.Errorf("2) got %s want %s", x, q)
}
// 3) aliasing of denominator
x = NewRat(2, 3)
x.SetFrac(x.Denom(), NewInt(2))
if x.Cmp(q) != 0 {
t.Errorf("3) got %s want %s", x, q)
}
// 4) aliasing of numerator and denominator
x = NewRat(2, 3)
x.SetFrac(x.Denom(), x.Num())
if x.Cmp(q) != 0 {
t.Errorf("4) got %s want %s", x, q)
}
// 5) numerator and denominator are the same
q = NewRat(1, 1)
x = new(Rat)
n := NewInt(7)
x.SetFrac(n, n)
if x.Cmp(q) != 0 {
t.Errorf("5) got %s want %s", x, q)
}
}
func TestIssue3521(t *testing.T) {
a := new(Int)
b := new(Int)
a.SetString("64375784358435883458348587", 0)
b.SetString("4789759874531", 0)
// 0) a raw zero value has 1 as denominator
zero := new(Rat)
one := NewInt(1)
if zero.Denom().Cmp(one) != 0 {
t.Errorf("0) got %s want %s", zero.Denom(), one)
}
// 1a) a zero value remains zero independent of denominator
x := new(Rat)
x.SetDenom(new(Int).Neg(b))
if x.Cmp(zero) != 0 {
t.Errorf("1a) got %s want %s", x, zero)
}
// 1b) a zero value may have a denominator != 0 and != 1
x.SetNum(a)
qab := new(Rat).SetFrac(a, new(Int).Neg(b)) // FIXME -ve compared to math/big
if x.Cmp(qab) != 0 {
t.Errorf("1b) got %s want %s", x, qab)
}
// 2a) an integral value becomes a fraction depending on denominator
x.SetFrac64(10, 2)
x.SetDenom(NewInt(3))
q53 := NewRat(5, 3)
if x.Cmp(q53) != 0 {
t.Errorf("2a) got %s want %s", x, q53)
}
// 2b) an integral value becomes a fraction depending on denominator
x = NewRat(10, 2)
x.SetDenom(NewInt(3))
if x.Cmp(q53) != 0 {
t.Errorf("2b) got %s want %s", x, q53)
}
// 3) changing the numerator/denominator of a Rat changes the Rat
x.SetFrac(a, b)
x.SetNum(NewInt(5))
x.SetDenom(NewInt(3))
if x.Cmp(q53) != 0 {
t.Errorf("3) got %s want %s", x, q53)
}
}
// Test inputs to Rat.SetString. The prefix "long:" causes the test
// to be skipped in --test.short mode. (The threshold is about 500us.)
var float64inputs = []string{
// Constants plundered from strconv/testfp.txt.
// Table 1: Stress Inputs for Conversion to 53-bit Binary, < 1/2 ULP
"5e+125",
"69e+267",
"999e-026",
"7861e-034",
"75569e-254",
"928609e-261",
"9210917e+080",
"84863171e+114",
"653777767e+273",
"5232604057e-298",
"27235667517e-109",
"653532977297e-123",
"3142213164987e-294",
"46202199371337e-072",
"231010996856685e-073",
"9324754620109615e+212",
"78459735791271921e+049",
"272104041512242479e+200",
"6802601037806061975e+198",
"20505426358836677347e-221",
"836168422905420598437e-234",
"4891559871276714924261e+222",
// Table 2: Stress Inputs for Conversion to 53-bit Binary, > 1/2 ULP
"9e-265",
"85e-037",
"623e+100",
"3571e+263",
"81661e+153",
"920657e-023",
"4603285e-024",
"87575437e-309",
"245540327e+122",
"6138508175e+120",
"83356057653e+193",
"619534293513e+124",
"2335141086879e+218",
"36167929443327e-159",
"609610927149051e-255",
"3743626360493413e-165",
"94080055902682397e-242",
"899810892172646163e+283",
"7120190517612959703e+120",
"25188282901709339043e-252",
"308984926168550152811e-052",
"6372891218502368041059e+064",
// Table 14: Stress Inputs for Conversion to 24-bit Binary, <1/2 ULP
"5e-20",
"67e+14",
"985e+15",
"7693e-42",
"55895e-16",
"996622e-44",
"7038531e-32",
"60419369e-46",
"702990899e-20",
"6930161142e-48",
"25933168707e+13",
"596428896559e+20",
// Table 15: Stress Inputs for Conversion to 24-bit Binary, >1/2 ULP
"3e-23",
"57e+18",
"789e-35",
"2539e-18",
"76173e+28",
"887745e-11",
"5382571e-37",
"82381273e-35",
"750486563e-38",
"3752432815e-39",
"75224575729e-45",
"459926601011e+15",
// Constants plundered from strconv/atof_test.go.
"0",
"1",
"+1",
"1e23",
"1E23",
"100000000000000000000000",
"1e-100",
"123456700",
"99999999999999974834176",
"100000000000000000000001",
"100000000000000008388608",
"100000000000000016777215",
"100000000000000016777216",
"-1",
"-0.1",
"-0", // NB: exception made for this input
"1e-20",
"625e-3",
// largest float64
"1.7976931348623157e308",
"-1.7976931348623157e308",
// next float64 - too large
"1.7976931348623159e308",
"-1.7976931348623159e308",
// the border is ...158079
// borderline - okay
"1.7976931348623158e308",
"-1.7976931348623158e308",
// borderline - too large
"1.797693134862315808e308",
"-1.797693134862315808e308",
// a little too large
"1e308",
"2e308",
"1e309",
// way too large
"1e310",
"-1e310",
"1e400",
"-1e400",
"long:1e400000",
"long:-1e400000",
// denormalized
"1e-305",
"1e-306",
"1e-307",
"1e-308",
"1e-309",
"1e-310",
"1e-322",
// smallest denormal
"5e-324",
"4e-324",
"3e-324",
// too small
"2e-324",
// way too small
"1e-350",
"long:1e-400000",
// way too small, negative
"-1e-350",
"long:-1e-400000",
// try to overflow exponent
// [Disabled: too slow and memory-hungry with rationals.]
// "1e-4294967296",
// "1e+4294967296",
// "1e-18446744073709551616",
// "1e+18446744073709551616",
// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
"2.2250738585072012e-308",
// http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/
"2.2250738585072011e-308",
// A very large number (initially wrongly parsed by the fast algorithm).
"4.630813248087435e+307",
// A different kind of very large number.
"22.222222222222222",
"long:2." + strings.Repeat("2", 4000) + "e+1",
// Exactly halfway between 1 and math.Nextafter(1, 2).
// Round to even (down).
"1.00000000000000011102230246251565404236316680908203125",
// Slightly lower; still round down.
"1.00000000000000011102230246251565404236316680908203124",
// Slightly higher; round up.
"1.00000000000000011102230246251565404236316680908203126",
// Slightly higher, but you have to read all the way to the end.
"long:1.00000000000000011102230246251565404236316680908203125" + strings.Repeat("0", 10000) + "1",
// Smallest denormal, 2^(-1022-52)
"4.940656458412465441765687928682213723651e-324",
// Half of smallest denormal, 2^(-1022-53)
"2.470328229206232720882843964341106861825e-324",
// A little more than the exact half of smallest denormal
// 2^-1075 + 2^-1100. (Rounds to 1p-1074.)
"2.470328302827751011111470718709768633275e-324",
// The exact halfway between smallest normal and largest denormal:
// 2^-1022 - 2^-1075. (Rounds to 2^-1022.)
"2.225073858507201136057409796709131975935e-308",
"1152921504606846975", // 1<<60 - 1
"-1152921504606846975", // -(1<<60 - 1)
"1152921504606846977", // 1<<60 + 1
"-1152921504606846977", // -(1<<60 + 1)
"1/3",
}
// isFinite reports whether f represents a finite rational value.
// It is equivalent to !math.IsNan(f) && !math.IsInf(f, 0).
func isFinite(f float64) bool {
return math.Abs(f) <= math.MaxFloat64
}
func TestFloat64SpecialCases(t *testing.T) {
for _, input := range float64inputs {
if strings.HasPrefix(input, "long:") {
if testing.Short() {
continue
}
input = input[len("long:"):]
}
r, ok := new(Rat).SetString(input)
if !ok {
t.Errorf("Rat.SetString(%q) failed", input)
continue
}
f, exact := r.Float64()
// 1. Check string -> Rat -> float64 conversions are
// consistent with strconv.ParseFloat.
// Skip this check if the input uses "a/b" rational syntax.
if !strings.Contains(input, "/") {
e, _ := strconv.ParseFloat(input, 64)
// Careful: negative Rats too small for
// float64 become -0, but Rat obviously cannot
// preserve the sign from SetString("-0").
switch {
case math.Float64bits(e) == math.Float64bits(f):
// Ok: bitwise equal.
case f == 0 && r.Num().BitLen() == 0:
// Ok: Rat(0) is equivalent to both +/- float64(0).
default:
t.Errorf("strconv.ParseFloat(%q) = %g (%b), want %g (%b); delta = %g", input, e, e, f, f, f-e)
}
}
if !isFinite(f) {
continue
}
// 2. Check f is best approximation to r.
if !checkIsBestApprox(t, f, r) {
// Append context information.
t.Errorf("(input was %q)", input)
}
// 3. Check f->R->f roundtrip is non-lossy.
checkNonLossyRoundtrip(t, f)
// 4. Check exactness using slow algorithm.
if wasExact := new(Rat).SetFloat64(f).Cmp(r) == 0; wasExact != exact {
t.Errorf("Rat.SetString(%q).Float64().exact = %t, want %t", input, exact, wasExact)
}
}
}
func TestFloat64Distribution(t *testing.T) {
// Generate a distribution of (sign, mantissa, exp) values
// broader than the float64 range, and check Rat.Float64()
// always picks the closest float64 approximation.
var add = []int64{
0,
1,
3,
5,
7,
9,
11,
}
var winc, einc = uint64(1), int(1) // soak test (~75s on x86-64)
if testing.Short() {
winc, einc = 10, 500 // quick test (~12ms on x86-64)
}
for _, sign := range "+-" {
for _, a := range add {
for wid := uint64(0); wid < 60; wid += winc {
b := int64(1<<wid + a)
if sign == '-' {
b = -b
}
for exp := -1100; exp < 1100; exp += einc {
num, den := NewInt(b), NewInt(1)
if exp > 0 {
num.Lsh(num, uint(exp))
} else {
den.Lsh(den, uint(-exp))
}
r := new(Rat).SetFrac(num, den)
f, _ := r.Float64()
if !checkIsBestApprox(t, f, r) {
// Append context information.
t.Errorf("(input was mantissa %#x, exp %d; f = %g (%b); f ~ %g; r = %v)",
b, exp, f, f, math.Ldexp(float64(b), exp), r)
}
checkNonLossyRoundtrip(t, f)
}
}
}
}
}
// TestFloat64NonFinite checks that SetFloat64 of a non-finite value
// returns nil.
func TestSetFloat64NonFinite(t *testing.T) {
for _, f := range []float64{math.NaN(), math.Inf(+1), math.Inf(-1)} {
var r Rat
if r2 := r.SetFloat64(f); r2 != nil {
t.Errorf("SetFloat64(%g) was %v, want nil", f, r2)
}
}
}
// checkNonLossyRoundtrip checks that a float->Rat->float roundtrip is
// non-lossy for finite f.
func checkNonLossyRoundtrip(t *testing.T, f float64) {
if !isFinite(f) {
return
}
r := new(Rat).SetFloat64(f)
if r == nil {
t.Errorf("Rat.SetFloat64(%g (%b)) == nil", f, f)
return
}
f2, exact := r.Float64()
if f != f2 || !exact {
t.Errorf("Rat.SetFloat64(%g).Float64() = %g (%b), %v, want %g (%b), %v; delta = %b",
f, f2, f2, exact, f, f, true, f2-f)
}
}
// delta returns the absolute difference between r and f.
func delta(r *Rat, f float64) *Rat {
d := new(Rat).Sub(r, new(Rat).SetFloat64(f))
return d.Abs(d)
}
// checkIsBestApprox checks that f is the best possible float64
// approximation of r.
// Returns true on success.
func checkIsBestApprox(t *testing.T, f float64, r *Rat) bool {
if math.Abs(f) >= math.MaxFloat64 {
// Cannot check +Inf, -Inf, nor the float next to them (MaxFloat64).
// But we have tests for these special cases.
return true
}
// r must be strictly between f0 and f1, the floats bracketing f.
f0 := math.Nextafter(f, math.Inf(-1))
f1 := math.Nextafter(f, math.Inf(+1))
// For f to be correct, r must be closer to f than to f0 or f1.
df := delta(r, f)
df0 := delta(r, f0)
df1 := delta(r, f1)
if df.Cmp(df0) > 0 {
t.Errorf("Rat(%v).Float64() = %g (%b), but previous float64 %g (%b) is closer", r, f, f, f0, f0)
return false
}
if df.Cmp(df1) > 0 {
t.Errorf("Rat(%v).Float64() = %g (%b), but next float64 %g (%b) is closer", r, f, f, f1, f1)
return false
}
if df.Cmp(df0) == 0 && !isEven(f) {
t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f0, f0)
return false
}
if df.Cmp(df1) == 0 && !isEven(f) {
t.Errorf("Rat(%v).Float64() = %g (%b); halfway should have rounded to %g (%b) instead", r, f, f, f1, f1)
return false
}
return true
}
func isEven(f float64) bool { return math.Float64bits(f)&1 == 0 }
func TestIsFinite(t *testing.T) {
finites := []float64{
1.0 / 3,
4891559871276714924261e+222,
math.MaxFloat64,
math.SmallestNonzeroFloat64,
-math.MaxFloat64,
-math.SmallestNonzeroFloat64,
}
for _, f := range finites {
if !isFinite(f) {
t.Errorf("!IsFinite(%g (%b))", f, f)
}
}
nonfinites := []float64{
math.NaN(),
math.Inf(-1),
math.Inf(+1),
}
for _, f := range nonfinites {
if isFinite(f) {
t.Errorf("IsFinite(%g, (%b))", f, f)
}
}
}