github.com/primecitizens/pcz/std@v0.2.1/core/bits/arith.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright 2023 The Prime Citizens 3 // 4 // Copyright 2017 The Go Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file. 7 8 package bits 9 10 import ( 11 "github.com/primecitizens/pcz/std/core/arch" 12 "github.com/primecitizens/pcz/std/core/assert" 13 ) 14 15 // Add returns the sum with carry of x, y and carry: sum = x + y + carry. 16 // The carry input must be 0 or 1; otherwise the behavior is undefined. 17 // The carryOut output is guaranteed to be 0 or 1. 18 // 19 // This function's execution time does not depend on the inputs. 20 func Add(x, y, carry uint) (sum, carryOut uint) { 21 if arch.UintBits == 32 { 22 s32, c32 := Add32(uint32(x), uint32(y), uint32(carry)) 23 return uint(s32), uint(c32) 24 } 25 s64, c64 := Add64(uint64(x), uint64(y), uint64(carry)) 26 return uint(s64), uint(c64) 27 } 28 29 // Add32 returns the sum with carry of x, y and carry: sum = x + y + carry. 30 // The carry input must be 0 or 1; otherwise the behavior is undefined. 31 // The carryOut output is guaranteed to be 0 or 1. 32 // 33 // This function's execution time does not depend on the inputs. 34 func Add32(x, y, carry uint32) (sum, carryOut uint32) { 35 sum64 := uint64(x) + uint64(y) + uint64(carry) 36 sum = uint32(sum64) 37 carryOut = uint32(sum64 >> 32) 38 return 39 } 40 41 // Add64 returns the sum with carry of x, y and carry: sum = x + y + carry. 42 // The carry input must be 0 or 1; otherwise the behavior is undefined. 43 // The carryOut output is guaranteed to be 0 or 1. 44 // 45 // This function's execution time does not depend on the inputs. 46 func Add64(x, y, carry uint64) (sum, carryOut uint64) { 47 sum = x + y + carry 48 // The sum will overflow if both top bits are set (x & y) or if one of them 49 // is (x | y), and a carry from the lower place happened. If such a carry 50 // happens, the top bit will be 1 + 0 + 1 = 0 (&^ sum). 51 carryOut = ((x & y) | ((x | y) &^ sum)) >> 63 52 return 53 } 54 55 // Sub returns the difference of x, y and borrow: diff = x - y - borrow. 56 // The borrow input must be 0 or 1; otherwise the behavior is undefined. 57 // The borrowOut output is guaranteed to be 0 or 1. 58 // 59 // This function's execution time does not depend on the inputs. 60 func Sub(x, y, borrow uint) (diff, borrowOut uint) { 61 if arch.UintBits == 32 { 62 d32, b32 := Sub32(uint32(x), uint32(y), uint32(borrow)) 63 return uint(d32), uint(b32) 64 } 65 d64, b64 := Sub64(uint64(x), uint64(y), uint64(borrow)) 66 return uint(d64), uint(b64) 67 } 68 69 // Sub32 returns the difference of x, y and borrow, diff = x - y - borrow. 70 // The borrow input must be 0 or 1; otherwise the behavior is undefined. 71 // The borrowOut output is guaranteed to be 0 or 1. 72 // 73 // This function's execution time does not depend on the inputs. 74 func Sub32(x, y, borrow uint32) (diff, borrowOut uint32) { 75 diff = x - y - borrow 76 // The difference will underflow if the top bit of x is not set and the top 77 // bit of y is set (^x & y) or if they are the same (^(x ^ y)) and a borrow 78 // from the lower place happens. If that borrow happens, the result will be 79 // 1 - 1 - 1 = 0 - 0 - 1 = 1 (& diff). 80 borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 31 81 return 82 } 83 84 // Sub64 returns the difference of x, y and borrow: diff = x - y - borrow. 85 // The borrow input must be 0 or 1; otherwise the behavior is undefined. 86 // The borrowOut output is guaranteed to be 0 or 1. 87 // 88 // This function's execution time does not depend on the inputs. 89 func Sub64(x, y, borrow uint64) (diff, borrowOut uint64) { 90 diff = x - y - borrow 91 // See Sub32 for the bit logic. 92 borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63 93 return 94 } 95 96 // --- Full-width multiply --- 97 98 // Mul returns the full-width product of x and y: (hi, lo) = x * y 99 // with the product bits' upper half returned in hi and the lower 100 // half returned in lo. 101 // 102 // This function's execution time does not depend on the inputs. 103 func Mul(x, y uint) (hi, lo uint) { 104 if arch.UintBits == 32 { 105 h, l := Mul32(uint32(x), uint32(y)) 106 return uint(h), uint(l) 107 } 108 h, l := Mul64(uint64(x), uint64(y)) 109 return uint(h), uint(l) 110 } 111 112 // Mul32 returns the 64-bit product of x and y: (hi, lo) = x * y 113 // with the product bits' upper half returned in hi and the lower 114 // half returned in lo. 115 // 116 // This function's execution time does not depend on the inputs. 117 func Mul32(x, y uint32) (hi, lo uint32) { 118 tmp := uint64(x) * uint64(y) 119 hi, lo = uint32(tmp>>32), uint32(tmp) 120 return 121 } 122 123 // Mul64 returns the 128-bit product of x and y: (hi, lo) = x * y 124 // with the product bits' upper half returned in hi and the lower 125 // half returned in lo. 126 // 127 // This function's execution time does not depend on the inputs. 128 func Mul64(x, y uint64) (hi, lo uint64) { 129 const mask32 = 1<<32 - 1 130 x0 := x & mask32 131 x1 := x >> 32 132 y0 := y & mask32 133 y1 := y >> 32 134 w0 := x0 * y0 135 t := x1*y0 + w0>>32 136 w1 := t & mask32 137 w2 := t >> 32 138 w1 += x0 * y1 139 hi = x1*y1 + w2 + w1>>32 140 lo = x * y 141 return 142 } 143 144 // --- Full-width divide --- 145 146 // Div returns the quotient and remainder of (hi, lo) divided by y: 147 // quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper 148 // half in parameter hi and the lower half in parameter lo. 149 // Div panics for y == 0 (division by zero) or y <= hi (quotient overflow). 150 func Div(hi, lo, y uint) (quo, rem uint) { 151 if arch.UintBits == 32 { 152 q, r := Div32(uint32(hi), uint32(lo), uint32(y)) 153 return uint(q), uint(r) 154 } 155 q, r := Div64(uint64(hi), uint64(lo), uint64(y)) 156 return uint(q), uint(r) 157 } 158 159 // Div32 returns the quotient and remainder of (hi, lo) divided by y: 160 // quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper 161 // half in parameter hi and the lower half in parameter lo. 162 // Div32 panics for y == 0 (division by zero) or y <= hi (quotient overflow). 163 func Div32(hi, lo, y uint32) (quo, rem uint32) { 164 if y != 0 && y <= hi { 165 assert.Panic(ErrOverflow{}) 166 } 167 z := uint64(hi)<<32 | uint64(lo) 168 quo, rem = uint32(z/uint64(y)), uint32(z%uint64(y)) 169 return 170 } 171 172 // Div64 returns the quotient and remainder of (hi, lo) divided by y: 173 // quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper 174 // half in parameter hi and the lower half in parameter lo. 175 // Div64 panics for y == 0 (division by zero) or y <= hi (quotient overflow). 176 func Div64(hi, lo, y uint64) (quo, rem uint64) { 177 if y == 0 { 178 assert.Panic(ErrDivideByZero{}) 179 } 180 if y <= hi { 181 assert.Panic(ErrOverflow{}) 182 } 183 184 // If high part is zero, we can directly return the results. 185 if hi == 0 { 186 return lo / y, lo % y 187 } 188 189 s := uint(LeadingZeros64(y)) 190 y <<= s 191 192 const ( 193 two32 = 1 << 32 194 mask32 = two32 - 1 195 ) 196 yn1 := y >> 32 197 yn0 := y & mask32 198 un32 := hi<<s | lo>>(64-s) 199 un10 := lo << s 200 un1 := un10 >> 32 201 un0 := un10 & mask32 202 q1 := un32 / yn1 203 rhat := un32 - q1*yn1 204 205 for q1 >= two32 || q1*yn0 > two32*rhat+un1 { 206 q1-- 207 rhat += yn1 208 if rhat >= two32 { 209 break 210 } 211 } 212 213 un21 := un32*two32 + un1 - q1*y 214 q0 := un21 / yn1 215 rhat = un21 - q0*yn1 216 217 for q0 >= two32 || q0*yn0 > two32*rhat+un0 { 218 q0-- 219 rhat += yn1 220 if rhat >= two32 { 221 break 222 } 223 } 224 225 return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s 226 } 227 228 // Rem returns the remainder of (hi, lo) divided by y. Rem panics for 229 // y == 0 (division by zero) but, unlike Div, it doesn't panic on a 230 // quotient overflow. 231 func Rem(hi, lo, y uint) uint { 232 if arch.UintBits == 32 { 233 return uint(Rem32(uint32(hi), uint32(lo), uint32(y))) 234 } 235 return uint(Rem64(uint64(hi), uint64(lo), uint64(y))) 236 } 237 238 // Rem32 returns the remainder of (hi, lo) divided by y. Rem32 panics 239 // for y == 0 (division by zero) but, unlike Div32, it doesn't panic 240 // on a quotient overflow. 241 func Rem32(hi, lo, y uint32) uint32 { 242 return uint32((uint64(hi)<<32 | uint64(lo)) % uint64(y)) 243 } 244 245 // Rem64 returns the remainder of (hi, lo) divided by y. Rem64 panics 246 // for y == 0 (division by zero) but, unlike Div64, it doesn't panic 247 // on a quotient overflow. 248 func Rem64(hi, lo, y uint64) uint64 { 249 // We scale down hi so that hi < y, then use Div64 to compute the 250 // rem with the guarantee that it won't panic on quotient overflow. 251 // Given that 252 // hi ≡ hi%y (mod y) 253 // we have 254 // hi<<64 + lo ≡ (hi%y)<<64 + lo (mod y) 255 _, rem := Div64(hi%y, lo, y) 256 return rem 257 }