safemath.gno
1.77 Kb · 92 lines
1// Package safemath is checked, non-negative int64 arithmetic for launchpad amounts.
2package safemath
3
4import "math/bits"
5
6const (
7 MaxInt64 int64 = 1<<63 - 1
8 BPSDenominator int64 = 10_000
9)
10
11func mustNonNegative(v int64) {
12 if v < 0 {
13 panic("launchpad/safemath: negative value")
14 }
15}
16
17func Add(a, b int64) int64 {
18 mustNonNegative(a)
19 mustNonNegative(b)
20 if a > MaxInt64-b {
21 panic("launchpad/safemath: overflow")
22 }
23 return a + b
24}
25
26func Sub(a, b int64) int64 {
27 mustNonNegative(a)
28 mustNonNegative(b)
29 if b > a {
30 panic("launchpad/safemath: underflow")
31 }
32 return a - b
33}
34
35func Mul(a, b int64) int64 {
36 mustNonNegative(a)
37 mustNonNegative(b)
38 if a != 0 && b > MaxInt64/a {
39 panic("launchpad/safemath: overflow")
40 }
41 return a * b
42}
43
44// MulDiv returns floor(a*b/d) using a 128-bit intermediate.
45func MulDiv(a, b, d int64) int64 {
46 q, _ := mulDivRem(a, b, d)
47 return q
48}
49
50// MulDivUp returns ceil(a*b/d) using a 128-bit intermediate.
51func MulDivUp(a, b, d int64) int64 {
52 q, rem := mulDivRem(a, b, d)
53 if rem {
54 if q == MaxInt64 {
55 panic("launchpad/safemath: overflow")
56 }
57 q++
58 }
59 return q
60}
61
62func mulDivRem(a, b, d int64) (int64, bool) {
63 mustNonNegative(a)
64 mustNonNegative(b)
65 if d <= 0 {
66 panic("launchpad/safemath: division by zero")
67 }
68 hi, lo := bits.Mul64(uint64(a), uint64(b))
69 if hi >= uint64(d) {
70 panic("launchpad/safemath: overflow")
71 }
72 q, r := bits.Div64(hi, lo, uint64(d))
73 if q > uint64(MaxInt64) {
74 panic("launchpad/safemath: overflow")
75 }
76 return int64(q), r != 0
77}
78
79// BPS returns floor(amount*bps/10000) for 0 <= bps <= 10000.
80func BPS(amount, bps int64) int64 {
81 if bps < 0 || bps > BPSDenominator {
82 panic("launchpad/safemath: basis points out of range")
83 }
84 return MulDiv(amount, bps, BPSDenominator)
85}
86
87func Min(a, b int64) int64 {
88 if a < b {
89 return a
90 }
91 return b
92}