Commit ded3ef28 authored by Tim Hockin's avatar Tim Hockin

Merge pull request #1185 from lavalamp/numeric

Numeric type for resources
parents f11f0de4 cd58e49c
......@@ -203,6 +203,10 @@
{
"ImportPath": "gopkg.in/v2/yaml",
"Rev": "d466437aa4adc35830964cffc5b5f262c63ddcb4"
},
{
"ImportPath": "speter.net/go/exp/math/dec/inf",
"Rev": "42ca6cd68aa922bc3f32f1e056e61b65945d9ad7"
}
]
}
Copyright (c) 2012 Péter Surányi. 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.
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.
----------------------------------------------------------------------
Portions of inf.Dec's source code have been derived from Go and are
covered by the following license:
----------------------------------------------------------------------
Copyright (c) 2009 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.
package inf
import (
"fmt"
"math/big"
"math/rand"
"sync"
"testing"
)
const maxcap = 1024 * 1024
const bits = 256
const maxscale = 32
var once sync.Once
var decInput [][2]Dec
var intInput [][2]big.Int
var initBench = func() {
decInput = make([][2]Dec, maxcap)
intInput = make([][2]big.Int, maxcap)
max := new(big.Int).Lsh(big.NewInt(1), bits)
r := rand.New(rand.NewSource(0))
for i := 0; i < cap(decInput); i++ {
decInput[i][0].SetUnscaledBig(new(big.Int).Rand(r, max)).
SetScale(Scale(r.Int31n(int32(2*maxscale-1)) - int32(maxscale)))
decInput[i][1].SetUnscaledBig(new(big.Int).Rand(r, max)).
SetScale(Scale(r.Int31n(int32(2*maxscale-1)) - int32(maxscale)))
}
for i := 0; i < cap(intInput); i++ {
intInput[i][0].Rand(r, max)
intInput[i][1].Rand(r, max)
}
}
func doBenchmarkDec1(b *testing.B, f func(z *Dec)) {
once.Do(initBench)
b.ResetTimer()
b.StartTimer()
for i := 0; i < b.N; i++ {
f(&decInput[i%maxcap][0])
}
}
func doBenchmarkDec2(b *testing.B, f func(x, y *Dec)) {
once.Do(initBench)
b.ResetTimer()
b.StartTimer()
for i := 0; i < b.N; i++ {
f(&decInput[i%maxcap][0], &decInput[i%maxcap][1])
}
}
func doBenchmarkInt1(b *testing.B, f func(z *big.Int)) {
once.Do(initBench)
b.ResetTimer()
b.StartTimer()
for i := 0; i < b.N; i++ {
f(&intInput[i%maxcap][0])
}
}
func doBenchmarkInt2(b *testing.B, f func(x, y *big.Int)) {
once.Do(initBench)
b.ResetTimer()
b.StartTimer()
for i := 0; i < b.N; i++ {
f(&intInput[i%maxcap][0], &intInput[i%maxcap][1])
}
}
func Benchmark_Dec_String(b *testing.B) {
doBenchmarkDec1(b, func(x *Dec) {
x.String()
})
}
func Benchmark_Dec_StringScan(b *testing.B) {
doBenchmarkDec1(b, func(x *Dec) {
s := x.String()
d := new(Dec)
fmt.Sscan(s, d)
})
}
func Benchmark_Dec_GobEncode(b *testing.B) {
doBenchmarkDec1(b, func(x *Dec) {
x.GobEncode()
})
}
func Benchmark_Dec_GobEnDecode(b *testing.B) {
doBenchmarkDec1(b, func(x *Dec) {
g, _ := x.GobEncode()
new(Dec).GobDecode(g)
})
}
func Benchmark_Dec_Add(b *testing.B) {
doBenchmarkDec2(b, func(x, y *Dec) {
ys := y.Scale()
y.SetScale(x.Scale())
_ = new(Dec).Add(x, y)
y.SetScale(ys)
})
}
func Benchmark_Dec_AddMixed(b *testing.B) {
doBenchmarkDec2(b, func(x, y *Dec) {
_ = new(Dec).Add(x, y)
})
}
func Benchmark_Dec_Sub(b *testing.B) {
doBenchmarkDec2(b, func(x, y *Dec) {
ys := y.Scale()
y.SetScale(x.Scale())
_ = new(Dec).Sub(x, y)
y.SetScale(ys)
})
}
func Benchmark_Dec_SubMixed(b *testing.B) {
doBenchmarkDec2(b, func(x, y *Dec) {
_ = new(Dec).Sub(x, y)
})
}
func Benchmark_Dec_Mul(b *testing.B) {
doBenchmarkDec2(b, func(x, y *Dec) {
_ = new(Dec).Mul(x, y)
})
}
func Benchmark_Dec_Mul_QuoExact(b *testing.B) {
doBenchmarkDec2(b, func(x, y *Dec) {
v := new(Dec).Mul(x, y)
_ = new(Dec).QuoExact(v, y)
})
}
func Benchmark_Dec_QuoRound_Fixed_Down(b *testing.B) {
doBenchmarkDec2(b, func(x, y *Dec) {
_ = new(Dec).QuoRound(x, y, 0, RoundDown)
})
}
func Benchmark_Dec_QuoRound_Fixed_HalfUp(b *testing.B) {
doBenchmarkDec2(b, func(x, y *Dec) {
_ = new(Dec).QuoRound(x, y, 0, RoundHalfUp)
})
}
func Benchmark_Int_String(b *testing.B) {
doBenchmarkInt1(b, func(x *big.Int) {
x.String()
})
}
func Benchmark_Int_StringScan(b *testing.B) {
doBenchmarkInt1(b, func(x *big.Int) {
s := x.String()
d := new(big.Int)
fmt.Sscan(s, d)
})
}
func Benchmark_Int_GobEncode(b *testing.B) {
doBenchmarkInt1(b, func(x *big.Int) {
x.GobEncode()
})
}
func Benchmark_Int_GobEnDecode(b *testing.B) {
doBenchmarkInt1(b, func(x *big.Int) {
g, _ := x.GobEncode()
new(big.Int).GobDecode(g)
})
}
func Benchmark_Int_Add(b *testing.B) {
doBenchmarkInt2(b, func(x, y *big.Int) {
_ = new(big.Int).Add(x, y)
})
}
func Benchmark_Int_Sub(b *testing.B) {
doBenchmarkInt2(b, func(x, y *big.Int) {
_ = new(big.Int).Sub(x, y)
})
}
func Benchmark_Int_Mul(b *testing.B) {
doBenchmarkInt2(b, func(x, y *big.Int) {
_ = new(big.Int).Mul(x, y)
})
}
func Benchmark_Int_Quo(b *testing.B) {
doBenchmarkInt2(b, func(x, y *big.Int) {
_ = new(big.Int).Quo(x, y)
})
}
func Benchmark_Int_QuoRem(b *testing.B) {
doBenchmarkInt2(b, func(x, y *big.Int) {
_, _ = new(big.Int).QuoRem(x, y, new(big.Int))
})
}
// +build go1.2
package inf
import (
"encoding"
"encoding/json"
"testing"
)
var _ encoding.TextMarshaler = new(Dec)
var _ encoding.TextUnmarshaler = new(Dec)
type Obj struct {
Val *Dec
}
func TestDecJsonMarshalUnmarshal(t *testing.T) {
o := Obj{Val: NewDec(123, 2)}
js, err := json.Marshal(o)
if err != nil {
t.Fatalf("json.Marshal(%v): got %v, want ok", o, err)
}
o2 := &Obj{}
err = json.Unmarshal(js, o2)
if err != nil {
t.Fatalf("json.Unmarshal(%#q): got %v, want ok", js, err)
}
if o.Val.Scale() != o2.Val.Scale() ||
o.Val.UnscaledBig().Cmp(o2.Val.UnscaledBig()) != 0 {
t.Fatalf("json.Unmarshal(json.Marshal(%v)): want %v, got %v", o, o, o2)
}
}
package inf
import (
"math/big"
"testing"
)
var decQuoRemZZZ = []struct {
z, x, y *Dec
r *big.Rat
srA, srB int
}{
// basic examples
{NewDec(1, 0), NewDec(2, 0), NewDec(2, 0), big.NewRat(0, 1), 0, 1},
{NewDec(15, 1), NewDec(3, 0), NewDec(2, 0), big.NewRat(0, 1), 0, 1},
{NewDec(1, 1), NewDec(1, 0), NewDec(10, 0), big.NewRat(0, 1), 0, 1},
{NewDec(0, 0), NewDec(2, 0), NewDec(3, 0), big.NewRat(2, 3), 1, 1},
{NewDec(0, 0), NewDec(2, 0), NewDec(6, 0), big.NewRat(1, 3), 1, 1},
{NewDec(1, 1), NewDec(2, 0), NewDec(12, 0), big.NewRat(2, 3), 1, 1},
// examples from the Go Language Specification
{NewDec(1, 0), NewDec(5, 0), NewDec(3, 0), big.NewRat(2, 3), 1, 1},
{NewDec(-1, 0), NewDec(-5, 0), NewDec(3, 0), big.NewRat(-2, 3), -1, 1},
{NewDec(-1, 0), NewDec(5, 0), NewDec(-3, 0), big.NewRat(-2, 3), 1, -1},
{NewDec(1, 0), NewDec(-5, 0), NewDec(-3, 0), big.NewRat(2, 3), -1, -1},
}
func TestDecQuoRem(t *testing.T) {
for i, a := range decQuoRemZZZ {
z, rA, rB := new(Dec), new(big.Int), new(big.Int)
s := scaleQuoExact{}.Scale(a.x, a.y)
z.quoRem(a.x, a.y, s, true, rA, rB)
if a.z.Cmp(z) != 0 || a.r.Cmp(new(big.Rat).SetFrac(rA, rB)) != 0 {
t.Errorf("#%d QuoRemZZZ got %v, %v, %v; expected %v, %v", i, z, rA, rB, a.z, a.r)
}
if a.srA != rA.Sign() || a.srB != rB.Sign() {
t.Errorf("#%d QuoRemZZZ wrong signs, got %v, %v; expected %v, %v", i, rA.Sign(), rB.Sign(), a.srA, a.srB)
}
}
}
package inf_test
import (
"fmt"
"log"
)
import "speter.net/go/exp/math/dec/inf"
func ExampleDec_SetString() {
d := new(inf.Dec)
d.SetString("012345.67890") // decimal; leading 0 ignored; trailing 0 kept
fmt.Println(d)
// Output: 12345.67890
}
func ExampleDec_Scan() {
// The Scan function is rarely used directly;
// the fmt package recognizes it as an implementation of fmt.Scanner.
d := new(inf.Dec)
_, err := fmt.Sscan("184467440.73709551617", d)
if err != nil {
log.Println("error scanning value:", err)
} else {
fmt.Println(d)
}
// Output: 184467440.73709551617
}
func ExampleDec_QuoRound_scale2RoundDown() {
// 10 / 3 is an infinite decimal; it has no exact Dec representation
x, y := inf.NewDec(10, 0), inf.NewDec(3, 0)
// use 2 digits beyond the decimal point, round towards 0
z := new(inf.Dec).QuoRound(x, y, 2, inf.RoundDown)
fmt.Println(z)
// Output: 3.33
}
func ExampleDec_QuoRound_scale2RoundCeil() {
// -42 / 400 is an finite decimal with 3 digits beyond the decimal point
x, y := inf.NewDec(-42, 0), inf.NewDec(400, 0)
// use 2 digits beyond decimal point, round towards positive infinity
z := new(inf.Dec).QuoRound(x, y, 2, inf.RoundCeil)
fmt.Println(z)
// Output: -0.10
}
func ExampleDec_QuoExact_ok() {
// 1 / 25 is a finite decimal; it has exact Dec representation
x, y := inf.NewDec(1, 0), inf.NewDec(25, 0)
z := new(inf.Dec).QuoExact(x, y)
fmt.Println(z)
// Output: 0.04
}
func ExampleDec_QuoExact_fail() {
// 1 / 3 is an infinite decimal; it has no exact Dec representation
x, y := inf.NewDec(1, 0), inf.NewDec(3, 0)
z := new(inf.Dec).QuoExact(x, y)
fmt.Println(z)
// Output: <nil>
}
package inf
import (
"math/big"
)
// Rounder represents a method for rounding the (possibly infinite decimal)
// result of a division to a finite Dec. It is used by Dec.Round() and
// Dec.Quo().
//
// See the Example for results of using each Rounder with some sample values.
//
type Rounder rounder
// See http://speleotrove.com/decimal/damodel.html#refround for more detailed
// definitions of these rounding modes.
var (
RoundDown Rounder // towards 0
RoundUp Rounder // away from 0
RoundFloor Rounder // towards -infinity
RoundCeil Rounder // towards +infinity
RoundHalfDown Rounder // to nearest; towards 0 if same distance
RoundHalfUp Rounder // to nearest; away from 0 if same distance
RoundHalfEven Rounder // to nearest; even last digit if same distance
)
// RoundExact is to be used in the case when rounding is not necessary.
// When used with Quo or Round, it returns the result verbatim when it can be
// expressed exactly with the given precision, and it returns nil otherwise.
// QuoExact is a shorthand for using Quo with RoundExact.
var RoundExact Rounder
type rounder interface {
// When UseRemainder() returns true, the Round() method is passed the
// remainder of the division, expressed as the numerator and denominator of
// a rational.
UseRemainder() bool
// Round sets the rounded value of a quotient to z, and returns z.
// quo is rounded down (truncated towards zero) to the scale obtained from
// the Scaler in Quo().
//
// When the remainder is not used, remNum and remDen are nil.
// When used, the remainder is normalized between -1 and 1; that is:
//
// -|remDen| < remNum < |remDen|
//
// remDen has the same sign as y, and remNum is zero or has the same sign
// as x.
Round(z, quo *Dec, remNum, remDen *big.Int) *Dec
}
type rndr struct {
useRem bool
round func(z, quo *Dec, remNum, remDen *big.Int) *Dec
}
func (r rndr) UseRemainder() bool {
return r.useRem
}
func (r rndr) Round(z, quo *Dec, remNum, remDen *big.Int) *Dec {
return r.round(z, quo, remNum, remDen)
}
var intSign = []*big.Int{big.NewInt(-1), big.NewInt(0), big.NewInt(1)}
func roundHalf(f func(c int, odd uint) (roundUp bool)) func(z, q *Dec, rA, rB *big.Int) *Dec {
return func(z, q *Dec, rA, rB *big.Int) *Dec {
z.Set(q)
brA, brB := rA.BitLen(), rB.BitLen()
if brA < brB-1 {
// brA < brB-1 => |rA| < |rB/2|
return z
}
roundUp := false
srA, srB := rA.Sign(), rB.Sign()
s := srA * srB
if brA == brB-1 {
rA2 := new(big.Int).Lsh(rA, 1)
if s < 0 {
rA2.Neg(rA2)
}
roundUp = f(rA2.Cmp(rB)*srB, z.UnscaledBig().Bit(0))
} else {
// brA > brB-1 => |rA| > |rB/2|
roundUp = true
}
if roundUp {
z.UnscaledBig().Add(z.UnscaledBig(), intSign[s+1])
}
return z
}
}
func init() {
RoundExact = rndr{true,
func(z, q *Dec, rA, rB *big.Int) *Dec {
if rA.Sign() != 0 {
return nil
}
return z.Set(q)
}}
RoundDown = rndr{false,
func(z, q *Dec, rA, rB *big.Int) *Dec {
return z.Set(q)
}}
RoundUp = rndr{true,
func(z, q *Dec, rA, rB *big.Int) *Dec {
z.Set(q)
if rA.Sign() != 0 {
z.UnscaledBig().Add(z.UnscaledBig(), intSign[rA.Sign()*rB.Sign()+1])
}
return z
}}
RoundFloor = rndr{true,
func(z, q *Dec, rA, rB *big.Int) *Dec {
z.Set(q)
if rA.Sign()*rB.Sign() < 0 {
z.UnscaledBig().Add(z.UnscaledBig(), intSign[0])
}
return z
}}
RoundCeil = rndr{true,
func(z, q *Dec, rA, rB *big.Int) *Dec {
z.Set(q)
if rA.Sign()*rB.Sign() > 0 {
z.UnscaledBig().Add(z.UnscaledBig(), intSign[2])
}
return z
}}
RoundHalfDown = rndr{true, roundHalf(
func(c int, odd uint) bool {
return c > 0
})}
RoundHalfUp = rndr{true, roundHalf(
func(c int, odd uint) bool {
return c >= 0
})}
RoundHalfEven = rndr{true, roundHalf(
func(c int, odd uint) bool {
return c > 0 || c == 0 && odd == 1
})}
}
package inf_test
import (
"fmt"
"os"
"text/tabwriter"
"speter.net/go/exp/math/dec/inf"
)
// This example displays the results of Dec.Round with each of the Rounders.
//
func ExampleRounder() {
var vals = []struct {
x string
s inf.Scale
}{
{"-0.18", 1}, {"-0.15", 1}, {"-0.12", 1}, {"-0.10", 1},
{"-0.08", 1}, {"-0.05", 1}, {"-0.02", 1}, {"0.00", 1},
{"0.02", 1}, {"0.05", 1}, {"0.08", 1}, {"0.10", 1},
{"0.12", 1}, {"0.15", 1}, {"0.18", 1},
}
var rounders = []struct {
name string
rounder inf.Rounder
}{
{"RoundDown", inf.RoundDown}, {"RoundUp", inf.RoundUp},
{"RoundCeil", inf.RoundCeil}, {"RoundFloor", inf.RoundFloor},
{"RoundHalfDown", inf.RoundHalfDown}, {"RoundHalfUp", inf.RoundHalfUp},
{"RoundHalfEven", inf.RoundHalfEven}, {"RoundExact", inf.RoundExact},
}
fmt.Println("The results of new(inf.Dec).Round(x, s, inf.RoundXXX):\n")
w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', tabwriter.AlignRight)
fmt.Fprint(w, "x\ts\t|\t")
for _, r := range rounders {
fmt.Fprintf(w, "%s\t", r.name[5:])
}
fmt.Fprintln(w)
for _, v := range vals {
fmt.Fprintf(w, "%s\t%d\t|\t", v.x, v.s)
for _, r := range rounders {
x, _ := new(inf.Dec).SetString(v.x)
z := new(inf.Dec).Round(x, v.s, r.rounder)
fmt.Fprintf(w, "%d\t", z)
}
fmt.Fprintln(w)
}
w.Flush()
// Output:
// The results of new(inf.Dec).Round(x, s, inf.RoundXXX):
//
// x s | Down Up Ceil Floor HalfDown HalfUp HalfEven Exact
// -0.18 1 | -0.1 -0.2 -0.1 -0.2 -0.2 -0.2 -0.2 <nil>
// -0.15 1 | -0.1 -0.2 -0.1 -0.2 -0.1 -0.2 -0.2 <nil>
// -0.12 1 | -0.1 -0.2 -0.1 -0.2 -0.1 -0.1 -0.1 <nil>
// -0.10 1 | -0.1 -0.1 -0.1 -0.1 -0.1 -0.1 -0.1 -0.1
// -0.08 1 | 0.0 -0.1 0.0 -0.1 -0.1 -0.1 -0.1 <nil>
// -0.05 1 | 0.0 -0.1 0.0 -0.1 0.0 -0.1 0.0 <nil>
// -0.02 1 | 0.0 -0.1 0.0 -0.1 0.0 0.0 0.0 <nil>
// 0.00 1 | 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
// 0.02 1 | 0.0 0.1 0.1 0.0 0.0 0.0 0.0 <nil>
// 0.05 1 | 0.0 0.1 0.1 0.0 0.0 0.1 0.0 <nil>
// 0.08 1 | 0.0 0.1 0.1 0.0 0.1 0.1 0.1 <nil>
// 0.10 1 | 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1
// 0.12 1 | 0.1 0.2 0.2 0.1 0.1 0.1 0.1 <nil>
// 0.15 1 | 0.1 0.2 0.2 0.1 0.1 0.2 0.2 <nil>
// 0.18 1 | 0.1 0.2 0.2 0.1 0.2 0.2 0.2 <nil>
}
package inf_test
import (
"math/big"
"testing"
"speter.net/go/exp/math/dec/inf"
)
var decRounderInputs = [...]struct {
quo *inf.Dec
rA, rB *big.Int
}{
// examples from go language spec
{inf.NewDec(1, 0), big.NewInt(2), big.NewInt(3)}, // 5 / 3
{inf.NewDec(-1, 0), big.NewInt(-2), big.NewInt(3)}, // -5 / 3
{inf.NewDec(-1, 0), big.NewInt(2), big.NewInt(-3)}, // 5 / -3
{inf.NewDec(1, 0), big.NewInt(-2), big.NewInt(-3)}, // -5 / -3
// examples from godoc
{inf.NewDec(-1, 1), big.NewInt(-8), big.NewInt(10)},
{inf.NewDec(-1, 1), big.NewInt(-5), big.NewInt(10)},
{inf.NewDec(-1, 1), big.NewInt(-2), big.NewInt(10)},
{inf.NewDec(0, 1), big.NewInt(-8), big.NewInt(10)},
{inf.NewDec(0, 1), big.NewInt(-5), big.NewInt(10)},
{inf.NewDec(0, 1), big.NewInt(-2), big.NewInt(10)},
{inf.NewDec(0, 1), big.NewInt(0), big.NewInt(1)},
{inf.NewDec(0, 1), big.NewInt(2), big.NewInt(10)},
{inf.NewDec(0, 1), big.NewInt(5), big.NewInt(10)},
{inf.NewDec(0, 1), big.NewInt(8), big.NewInt(10)},
{inf.NewDec(1, 1), big.NewInt(2), big.NewInt(10)},
{inf.NewDec(1, 1), big.NewInt(5), big.NewInt(10)},
{inf.NewDec(1, 1), big.NewInt(8), big.NewInt(10)},
}
var decRounderResults = [...]struct {
rounder inf.Rounder
results [len(decRounderInputs)]*inf.Dec
}{
{inf.RoundExact, [...]*inf.Dec{nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil,
inf.NewDec(0, 1), nil, nil, nil, nil, nil, nil}},
{inf.RoundDown, [...]*inf.Dec{
inf.NewDec(1, 0), inf.NewDec(-1, 0), inf.NewDec(-1, 0), inf.NewDec(1, 0),
inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1),
inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
inf.NewDec(0, 1),
inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1)}},
{inf.RoundUp, [...]*inf.Dec{
inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0),
inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-2, 1),
inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1),
inf.NewDec(0, 1),
inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1),
inf.NewDec(2, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}},
{inf.RoundHalfDown, [...]*inf.Dec{
inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0),
inf.NewDec(-2, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1),
inf.NewDec(-1, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
inf.NewDec(0, 1),
inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(1, 1),
inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(2, 1)}},
{inf.RoundHalfUp, [...]*inf.Dec{
inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0),
inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-1, 1),
inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(0, 1),
inf.NewDec(0, 1),
inf.NewDec(0, 1), inf.NewDec(1, 1), inf.NewDec(1, 1),
inf.NewDec(1, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}},
{inf.RoundHalfEven, [...]*inf.Dec{
inf.NewDec(2, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(2, 0),
inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-1, 1),
inf.NewDec(-1, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
inf.NewDec(0, 1),
inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(1, 1),
inf.NewDec(1, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}},
{inf.RoundFloor, [...]*inf.Dec{
inf.NewDec(1, 0), inf.NewDec(-2, 0), inf.NewDec(-2, 0), inf.NewDec(1, 0),
inf.NewDec(-2, 1), inf.NewDec(-2, 1), inf.NewDec(-2, 1),
inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1),
inf.NewDec(0, 1),
inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1)}},
{inf.RoundCeil, [...]*inf.Dec{
inf.NewDec(2, 0), inf.NewDec(-1, 0), inf.NewDec(-1, 0), inf.NewDec(2, 0),
inf.NewDec(-1, 1), inf.NewDec(-1, 1), inf.NewDec(-1, 1),
inf.NewDec(0, 1), inf.NewDec(0, 1), inf.NewDec(0, 1),
inf.NewDec(0, 1),
inf.NewDec(1, 1), inf.NewDec(1, 1), inf.NewDec(1, 1),
inf.NewDec(2, 1), inf.NewDec(2, 1), inf.NewDec(2, 1)}},
}
func TestDecRounders(t *testing.T) {
for i, a := range decRounderResults {
for j, input := range decRounderInputs {
q := new(inf.Dec).Set(input.quo)
rA, rB := new(big.Int).Set(input.rA), new(big.Int).Set(input.rB)
res := a.rounder.Round(new(inf.Dec), q, rA, rB)
if a.results[j] == nil && res == nil {
continue
}
if (a.results[j] == nil && res != nil) ||
(a.results[j] != nil && res == nil) ||
a.results[j].Cmp(res) != 0 {
t.Errorf("#%d,%d Rounder got %v; expected %v", i, j, res, a.results[j])
}
}
}
}
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package resource_test
import (
"fmt"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
)
func ExampleFormat() {
memorySize := resource.NewQuantity(5*1024*1024*1024, resource.BinarySI)
fmt.Printf("memorySize = %v\n", memorySize)
diskSize := resource.NewQuantity(5*1000*1000*1000, resource.DecimalSI)
fmt.Printf("diskSize = %v\n", diskSize)
cores := resource.NewMilliQuantity(5300, resource.DecimalSI)
fmt.Printf("cores = %v\n", cores)
// Output:
// memorySize = 5Gi
// diskSize = 5G
// cores = 5300m
}
func ExampleParseOrDie() {
memorySize := resource.ParseOrDie("5Gi")
fmt.Printf("memorySize = %v (%v)\n", memorySize.Value(), memorySize.Format)
diskSize := resource.ParseOrDie("5G")
fmt.Printf("diskSize = %v (%v)\n", diskSize.Value(), diskSize.Format)
cores := resource.ParseOrDie("5300m")
fmt.Printf("milliCores = %v (%v)\n", cores.MilliValue(), cores.Format)
cores2 := resource.ParseOrDie("5.4")
fmt.Printf("milliCores = %v (%v)\n", cores2.MilliValue(), cores2.Format)
// Output:
// memorySize = 5368709120 (BinarySI)
// diskSize = 5000000000 (DecimalSI)
// milliCores = 5300 (DecimalSI)
// milliCores = 5400 (DecimalSI)
}
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package resource
import (
"strconv"
)
type suffix string
// suffixer can interpret and construct suffixes.
type suffixer interface {
interpret(suffix) (base, exponent int, fmt Format, ok bool)
construct(base, exponent int, fmt Format) (s suffix, ok bool)
}
// quantitySuffixer handles suffixes for all three formats that quantity
// can handle.
var quantitySuffixer = newSuffixer()
type bePair struct {
base, exponent int
}
type listSuffixer struct {
suffixToBE map[suffix]bePair
beToSuffix map[bePair]suffix
}
func (ls *listSuffixer) addSuffix(s suffix, pair bePair) {
if ls.suffixToBE == nil {
ls.suffixToBE = map[suffix]bePair{}
}
if ls.beToSuffix == nil {
ls.beToSuffix = map[bePair]suffix{}
}
ls.suffixToBE[s] = pair
ls.beToSuffix[pair] = s
}
func (ls *listSuffixer) lookup(s suffix) (base, exponent int, ok bool) {
pair, ok := ls.suffixToBE[s]
if !ok {
return 0, 0, false
}
return pair.base, pair.exponent, true
}
func (ls *listSuffixer) construct(base, exponent int) (s suffix, ok bool) {
s, ok = ls.beToSuffix[bePair{base, exponent}]
return
}
type suffixHandler struct {
decSuffixes listSuffixer
binSuffixes listSuffixer
}
func newSuffixer() suffixer {
sh := &suffixHandler{}
sh.binSuffixes.addSuffix("Ki", bePair{2, 10})
sh.binSuffixes.addSuffix("Mi", bePair{2, 20})
sh.binSuffixes.addSuffix("Gi", bePair{2, 30})
sh.binSuffixes.addSuffix("Ti", bePair{2, 40})
sh.binSuffixes.addSuffix("Pi", bePair{2, 50})
sh.binSuffixes.addSuffix("Ei", bePair{2, 60})
// Don't emit an error when trying to produce
// a suffix for 2^0.
sh.decSuffixes.addSuffix("", bePair{2, 0})
sh.decSuffixes.addSuffix("m", bePair{10, -3})
sh.decSuffixes.addSuffix("", bePair{10, 0})
sh.decSuffixes.addSuffix("k", bePair{10, 3})
sh.decSuffixes.addSuffix("M", bePair{10, 6})
sh.decSuffixes.addSuffix("G", bePair{10, 9})
sh.decSuffixes.addSuffix("T", bePair{10, 12})
sh.decSuffixes.addSuffix("P", bePair{10, 15})
sh.decSuffixes.addSuffix("E", bePair{10, 18})
return sh
}
func (sh *suffixHandler) construct(base, exponent int, fmt Format) (s suffix, ok bool) {
switch fmt {
case DecimalSI:
return sh.decSuffixes.construct(base, exponent)
case BinarySI:
return sh.binSuffixes.construct(base, exponent)
case DecimalExponent:
if base != 10 {
return "", false
}
if exponent == 0 {
return "", true
}
return suffix("e" + strconv.FormatInt(int64(exponent), 10)), true
}
return "", false
}
func (sh *suffixHandler) interpret(suffix suffix) (base, exponent int, fmt Format, ok bool) {
// Try lookup tables first
if b, e, ok := sh.decSuffixes.lookup(suffix); ok {
return b, e, DecimalSI, true
}
if b, e, ok := sh.binSuffixes.lookup(suffix); ok {
return b, e, BinarySI, true
}
if len(suffix) > 1 && (suffix[0] == 'E' || suffix[0] == 'e') {
parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64)
if err != nil {
return 0, 0, DecimalExponent, false
}
return 10, int(parsed), DecimalExponent, true
}
return 0, 0, DecimalExponent, false
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment