1
2
3
4
5 package spec
6
7 import (
8 "math"
9 "math/bits"
10 )
11
12 func isSigned[T Ints | Uints]() bool {
13 return T(0)-1 < 0
14 }
15
16 func maxVal[T Ints | Uints]() T {
17 if isSigned[T]() {
18 var zero T
19 switch any(zero).(type) {
20 case int8:
21 return any(int8(math.MaxInt8)).(T)
22 case int16:
23 return any(int16(math.MaxInt16)).(T)
24 case int32:
25 return any(int32(math.MaxInt32)).(T)
26 case int64:
27 return any(int64(math.MaxInt64)).(T)
28 }
29 panic("unhandled type")
30 }
31 return ^T(0)
32 }
33
34 func minVal[T Ints | Uints]() T {
35 if isSigned[T]() {
36 return ^maxVal[T]()
37 }
38 return 0
39 }
40
41
42 func saturate[T Ints | Uints, U Ints | Uints](x T) U {
43 if isSigned[T]() {
44 return saturateS[U](int64(x))
45 }
46 return saturateU[U](uint64(x))
47 }
48
49
50 func saturateS[T Ints | Uints](x int64) T {
51 if int64(T(x)) == x && (x >= 0 || isSigned[T]()) {
52
53 return T(x)
54 }
55
56
57 if x > 0 {
58 return maxVal[T]()
59 }
60 return minVal[T]()
61 }
62
63
64 func saturateU[T Ints | Uints](x uint64) T {
65 if x < uint64(maxVal[T]()) {
66 return T(x)
67 }
68 return maxVal[T]()
69 }
70
71 func addSaturated[T Ints | Uints](x, y T) T {
72 if isSigned[T]() {
73 return saturateS[T](addSaturatedSSS64(int64(x), int64(y)))
74 }
75 sum, carry := bits.Add64(uint64(x), uint64(y), 0)
76 if carry > 0 {
77 return maxVal[T]()
78 }
79 return saturateU[T](sum)
80 }
81
82 func addSaturatedSSS64(x, y int64) int64 {
83 sum := x + y
84
85
86
87
88
89 if (x^sum)&(y^sum) < 0 {
90 if x > 0 {
91 return math.MaxInt64
92 }
93 return math.MinInt64
94 }
95
96 return sum
97 }
98
99 func mulSaturatedUSS[X Uints, Y Ints](x X, y Y) Y {
100
101 z := mulSaturatedUSS64(uint64(x), int64(y))
102 return saturateS[Y](z)
103 }
104
105 func mulSaturatedUSS64(x uint64, y int64) int64 {
106 if x == 0 || y == 0 {
107 return 0
108 }
109
110
111 var absI uint64
112 if y == math.MinInt64 {
113 absI = math.MaxInt64 + 1
114 } else if y < 0 {
115 absI = uint64(-y)
116 } else {
117 absI = uint64(y)
118 }
119
120
121 hi, lo := bits.Mul64(x, absI)
122
123 if y > 0 {
124
125 if hi > 0 || lo >= math.MaxInt64 {
126 return math.MaxInt64
127 }
128 return int64(lo)
129 } else {
130
131 if hi > 0 || lo >= uint64(math.MaxInt64)+1 {
132 return math.MinInt64
133 }
134 return -int64(lo)
135 }
136 }
137
View as plain text