1package inf
2
3import (
4	"math/big"
5)
6
7// Rounder represents a method for rounding the (possibly infinite decimal)
8// result of a division to a finite Dec. It is used by Dec.Round() and
9// Dec.Quo().
10//
11// See the Example for results of using each Rounder with some sample values.
12//
13type Rounder rounder
14
15// See http://speleotrove.com/decimal/damodel.html#refround for more detailed
16// definitions of these rounding modes.
17var (
18	RoundDown     Rounder // towards 0
19	RoundUp       Rounder // away from 0
20	RoundFloor    Rounder // towards -infinity
21	RoundCeil     Rounder // towards +infinity
22	RoundHalfDown Rounder // to nearest; towards 0 if same distance
23	RoundHalfUp   Rounder // to nearest; away from 0 if same distance
24	RoundHalfEven Rounder // to nearest; even last digit if same distance
25)
26
27// RoundExact is to be used in the case when rounding is not necessary.
28// When used with Quo or Round, it returns the result verbatim when it can be
29// expressed exactly with the given precision, and it returns nil otherwise.
30// QuoExact is a shorthand for using Quo with RoundExact.
31var RoundExact Rounder
32
33type rounder interface {
34
35	// When UseRemainder() returns true, the Round() method is passed the
36	// remainder of the division, expressed as the numerator and denominator of
37	// a rational.
38	UseRemainder() bool
39
40	// Round sets the rounded value of a quotient to z, and returns z.
41	// quo is rounded down (truncated towards zero) to the scale obtained from
42	// the Scaler in Quo().
43	//
44	// When the remainder is not used, remNum and remDen are nil.
45	// When used, the remainder is normalized between -1 and 1; that is:
46	//
47	//  -|remDen| < remNum < |remDen|
48	//
49	// remDen has the same sign as y, and remNum is zero or has the same sign
50	// as x.
51	Round(z, quo *Dec, remNum, remDen *big.Int) *Dec
52}
53
54type rndr struct {
55	useRem bool
56	round  func(z, quo *Dec, remNum, remDen *big.Int) *Dec
57}
58
59func (r rndr) UseRemainder() bool {
60	return r.useRem
61}
62
63func (r rndr) Round(z, quo *Dec, remNum, remDen *big.Int) *Dec {
64	return r.round(z, quo, remNum, remDen)
65}
66
67var intSign = []*big.Int{big.NewInt(-1), big.NewInt(0), big.NewInt(1)}
68
69func roundHalf(f func(c int, odd uint) (roundUp bool)) func(z, q *Dec, rA, rB *big.Int) *Dec {
70	return func(z, q *Dec, rA, rB *big.Int) *Dec {
71		z.Set(q)
72		brA, brB := rA.BitLen(), rB.BitLen()
73		if brA < brB-1 {
74			// brA < brB-1 => |rA| < |rB/2|
75			return z
76		}
77		roundUp := false
78		srA, srB := rA.Sign(), rB.Sign()
79		s := srA * srB
80		if brA == brB-1 {
81			rA2 := new(big.Int).Lsh(rA, 1)
82			if s < 0 {
83				rA2.Neg(rA2)
84			}
85			roundUp = f(rA2.Cmp(rB)*srB, z.UnscaledBig().Bit(0))
86		} else {
87			// brA > brB-1 => |rA| > |rB/2|
88			roundUp = true
89		}
90		if roundUp {
91			z.UnscaledBig().Add(z.UnscaledBig(), intSign[s+1])
92		}
93		return z
94	}
95}
96
97func init() {
98	RoundExact = rndr{true,
99		func(z, q *Dec, rA, rB *big.Int) *Dec {
100			if rA.Sign() != 0 {
101				return nil
102			}
103			return z.Set(q)
104		}}
105	RoundDown = rndr{false,
106		func(z, q *Dec, rA, rB *big.Int) *Dec {
107			return z.Set(q)
108		}}
109	RoundUp = rndr{true,
110		func(z, q *Dec, rA, rB *big.Int) *Dec {
111			z.Set(q)
112			if rA.Sign() != 0 {
113				z.UnscaledBig().Add(z.UnscaledBig(), intSign[rA.Sign()*rB.Sign()+1])
114			}
115			return z
116		}}
117	RoundFloor = rndr{true,
118		func(z, q *Dec, rA, rB *big.Int) *Dec {
119			z.Set(q)
120			if rA.Sign()*rB.Sign() < 0 {
121				z.UnscaledBig().Add(z.UnscaledBig(), intSign[0])
122			}
123			return z
124		}}
125	RoundCeil = rndr{true,
126		func(z, q *Dec, rA, rB *big.Int) *Dec {
127			z.Set(q)
128			if rA.Sign()*rB.Sign() > 0 {
129				z.UnscaledBig().Add(z.UnscaledBig(), intSign[2])
130			}
131			return z
132		}}
133	RoundHalfDown = rndr{true, roundHalf(
134		func(c int, odd uint) bool {
135			return c > 0
136		})}
137	RoundHalfUp = rndr{true, roundHalf(
138		func(c int, odd uint) bool {
139			return c >= 0
140		})}
141	RoundHalfEven = rndr{true, roundHalf(
142		func(c int, odd uint) bool {
143			return c > 0 || c == 0 && odd == 1
144		})}
145}
146