-
Notifications
You must be signed in to change notification settings - Fork 0
/
conv.go
51 lines (44 loc) · 1.25 KB
/
conv.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package ucum
import (
"fmt"
"math/big"
)
type Converter struct {
ratio big.Rat
}
func NewConverter(from, to Unit) (*Converter, error) {
a := Normalize(from).u
b := Normalize(to).u
if len(a.Components) != len(b.Components) {
return nil, fmt.Errorf("ucum: %q is not convertible to %q", from.String(), to.String())
}
ratio := new(big.Rat).SetInt(bigOne)
for key, expA := range a.Components {
expB, exists := b.Components[key] // normalized units are stripped from annotations, so we can look up directly by key
if !exists {
return nil, fmt.Errorf("ucum: %q is not convertible to %q", from.String(), to.String())
}
ratio.Mul(ratio, big.NewRat(int64(expB), int64(expA)))
}
ratio.Mul(ratio, b.Coeff)
ratio.Quo(ratio, a.Coeff)
return &Converter{ratio: *ratio}, nil
}
var (
bigZero = big.NewInt(0)
bigOne = big.NewInt(1)
)
func (c *Converter) Conv(val *big.Int) (converted *big.Int, exact bool) {
ret := new(big.Int).Mul(val, c.ratio.Num())
if c.ratio.IsInt() {
return ret, true
}
_, rem := ret.QuoRem(val, c.ratio.Denom(), new(big.Int))
return ret, rem.Cmp(bigZero) == 0
}
func Conv(val *big.Int, from, to Unit) (*big.Int, error) {
if from.u.Orig != "" && from.u.Orig == to.u.Orig {
return (&big.Int{}).Set(val), nil
}
return nil, nil
}