Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

arithmetic.gno

10.16 Kb · 452 lines
  1// arithmetic provides arithmetic operations for Uint objects.
  2// This includes basic binary operations such as addition, subtraction, multiplication, division, and modulo operations
  3// as well as overflow checks, and negation. These functions are essential for numeric
  4// calculations using 256-bit unsigned integers.
  5package uint256
  6
  7import (
  8	"math/bits"
  9)
 10
 11// Add sets z to the sum x+y and returns z.
 12func (z *Uint) Add(x, y *Uint) *Uint {
 13	var carry uint64
 14	z[0], carry = bits.Add64(x[0], y[0], 0)
 15	z[1], carry = bits.Add64(x[1], y[1], carry)
 16	z[2], carry = bits.Add64(x[2], y[2], carry)
 17	z[3], _ = bits.Add64(x[3], y[3], carry)
 18	return z
 19}
 20
 21// AddOverflow sets z to the sum x+y and returns z and true if overflow occurred.
 22func (z *Uint) AddOverflow(x, y *Uint) (*Uint, bool) {
 23	var carry uint64
 24	z[0], carry = bits.Add64(x[0], y[0], 0)
 25	z[1], carry = bits.Add64(x[1], y[1], carry)
 26	z[2], carry = bits.Add64(x[2], y[2], carry)
 27	z[3], carry = bits.Add64(x[3], y[3], carry)
 28	return z, carry != 0
 29}
 30
 31// Sub sets z to the difference x-y and returns z.
 32func (z *Uint) Sub(x, y *Uint) *Uint {
 33	var carry uint64
 34	z[0], carry = bits.Sub64(x[0], y[0], 0)
 35	z[1], carry = bits.Sub64(x[1], y[1], carry)
 36	z[2], carry = bits.Sub64(x[2], y[2], carry)
 37	z[3], _ = bits.Sub64(x[3], y[3], carry)
 38	return z
 39}
 40
 41// SubOverflow sets z to the difference x-y and returns z and true if underflow occurred.
 42func (z *Uint) SubOverflow(x, y *Uint) (*Uint, bool) {
 43	var carry uint64
 44	z[0], carry = bits.Sub64(x[0], y[0], 0)
 45	z[1], carry = bits.Sub64(x[1], y[1], carry)
 46	z[2], carry = bits.Sub64(x[2], y[2], carry)
 47	z[3], carry = bits.Sub64(x[3], y[3], carry)
 48	return z, carry != 0
 49}
 50
 51// Neg returns -x mod 2^256.
 52func (z *Uint) Neg(x *Uint) *Uint {
 53	return z.Sub(Zero(), x)
 54}
 55
 56// Mul sets z to the product x*y and returns z.
 57func (z *Uint) Mul(x, y *Uint) *Uint {
 58	var (
 59		res              Uint
 60		carry            uint64
 61		res1, res2, res3 uint64
 62	)
 63
 64	carry, res[0] = bits.Mul64(x[0], y[0])
 65	carry, res1 = umulHop(carry, x[1], y[0])
 66	carry, res2 = umulHop(carry, x[2], y[0])
 67	res3 = x[3]*y[0] + carry
 68
 69	carry, res[1] = umulHop(res1, x[0], y[1])
 70	carry, res2 = umulStep(res2, x[1], y[1], carry)
 71	res3 = res3 + x[2]*y[1] + carry
 72
 73	carry, res[2] = umulHop(res2, x[0], y[2])
 74	res3 = res3 + x[1]*y[2] + carry
 75
 76	res[3] = res3 + x[0]*y[3]
 77
 78	return z.Set(&res)
 79}
 80
 81// MulOverflow sets z to the product x*y and returns z and true if overflow occurred.
 82func (z *Uint) MulOverflow(x, y *Uint) (*Uint, bool) {
 83	p := umul(x, y)
 84	copy(z[:], p[:4])
 85	return z, (p[4] | p[5] | p[6] | p[7]) != 0
 86}
 87
 88// Div sets z to the quotient x/y and returns z.
 89// It panics if y == 0.
 90func (z *Uint) Div(x, y *Uint) *Uint {
 91	if y.IsZero() {
 92		panic("division by zero")
 93	}
 94	if y.Gt(x) {
 95		return z.Clear()
 96	}
 97	if x.Eq(y) {
 98		return z.SetOne()
 99	}
100	// Shortcut some cases
101	if x.IsUint64() {
102		return z.SetUint64(x.Uint64() / y.Uint64())
103	}
104
105	// At this point, we know
106	// x/y ; x > y > 0
107
108	var quot Uint
109	udivrem(quot[:], x[:], y)
110	return z.Set(&quot)
111}
112
113// Mod sets z to the modulus x%y and returns z.
114// It panics if y == 0.
115func (z *Uint) Mod(x, y *Uint) *Uint {
116	if y.IsZero() {
117		panic("modulo by zero")
118	}
119	if x.IsZero() {
120		return z.Clear()
121	}
122	switch x.Cmp(y) {
123	case -1:
124		// x < y
125		copy(z[:], x[:])
126		return z
127	case 0:
128		// x == y
129		return z.Clear() // They are equal
130	}
131
132	// At this point:
133	// x != 0
134	// y != 0
135	// x > y
136
137	// Shortcut trivial case
138	if x.IsUint64() {
139		return z.SetUint64(x.Uint64() % y.Uint64())
140	}
141
142	var quot Uint
143	*z = udivrem(quot[:], x[:], y)
144	return z
145}
146
147// MulMod sets z to (x * y) mod m and returns z.
148// It panics if m == 0.
149func (z *Uint) MulMod(x, y, m *Uint) *Uint {
150	if m.IsZero() {
151		panic("modulo by zero")
152	}
153	if x.IsZero() || y.IsZero() {
154		return z.Clear()
155	}
156	p := umul(x, y)
157
158	if m[3] != 0 {
159		mu := Reciprocal(m)
160		r := reduce4(p, m, mu)
161		return z.Set(&r)
162	}
163
164	var (
165		pl Uint
166		ph Uint
167	)
168
169	pl[0], pl[1], pl[2], pl[3] = p[0], p[1], p[2], p[3]
170	ph[0], ph[1], ph[2], ph[3] = p[4], p[5], p[6], p[7]
171
172	// If the multiplication is within 256 bits use Mod().
173	if ph.IsZero() {
174		return z.Mod(&pl, m)
175	}
176
177	var quot [8]uint64
178	rem := udivrem(quot[:], p[:], m)
179	return z.Set(&rem)
180}
181
182// DivMod sets z to the quotient x/y and m to the modulus x%y, returning the pair (z, m).
183// It panics if y == 0.
184func (z *Uint) DivMod(x, y, m *Uint) (*Uint, *Uint) {
185	if y.IsZero() {
186		panic("division by zero")
187	}
188
189	switch x.Cmp(y) {
190	case -1:
191		// x < y
192		return z.Clear(), m.Set(x)
193	case 0:
194		// x == y
195		return z.SetOne(), m.Clear()
196	}
197
198	// At this point:
199	// x != 0
200	// y != 0
201	// x > y
202
203	// Shortcut trivial case
204	if x.IsUint64() {
205		x0, y0 := x.Uint64(), y.Uint64()
206		return z.SetUint64(x0 / y0), m.SetUint64(x0 % y0)
207	}
208
209	var quot Uint
210	*m = udivrem(quot[:], x[:], y)
211	*z = quot
212	return z, m
213}
214
215// udivrem divides u by d and produces both quotient and remainder.
216// The quotient is stored in provided quot - len(u)-len(d)+1 words.
217// It loosely follows the Knuth's division algorithm (sometimes referenced as "schoolbook" division) using 64-bit words.
218// See Knuth, Volume 2, section 4.3.1, Algorithm D.
219func udivrem(quot, u []uint64, d *Uint) (rem Uint) {
220	var dLen int
221	for i := len(d) - 1; i >= 0; i-- {
222		if d[i] != 0 {
223			dLen = i + 1
224			break
225		}
226	}
227
228	shift := uint(bits.LeadingZeros64(d[dLen-1]))
229
230	var dnStorage Uint
231	dn := dnStorage[:dLen]
232	for i := dLen - 1; i > 0; i-- {
233		dn[i] = (d[i] << shift) | (d[i-1] >> (64 - shift))
234	}
235	dn[0] = d[0] << shift
236
237	var uLen int
238	for i := len(u) - 1; i >= 0; i-- {
239		if u[i] != 0 {
240			uLen = i + 1
241			break
242		}
243	}
244
245	if uLen < dLen {
246		copy(rem[:], u)
247		return rem
248	}
249
250	var unStorage [9]uint64
251	un := unStorage[:uLen+1]
252	un[uLen] = u[uLen-1] >> (64 - shift)
253	for i := uLen - 1; i > 0; i-- {
254		un[i] = (u[i] << shift) | (u[i-1] >> (64 - shift))
255	}
256	un[0] = u[0] << shift
257
258	if dLen == 1 {
259		r := udivremBy1(quot, un, dn[0])
260		rem.SetUint64(r >> shift)
261		return rem
262	}
263
264	udivremKnuth(quot, un, dn)
265
266	for i := 0; i < dLen-1; i++ {
267		rem[i] = (un[i] >> shift) | (un[i+1] << (64 - shift))
268	}
269	rem[dLen-1] = un[dLen-1] >> shift
270
271	return rem
272}
273
274// umul computes full 256 x 256 -> 512 multiplication.
275func umul(x, y *Uint) [8]uint64 {
276	var res [8]uint64
277
278	topX := highestNonZeroWord(x)
279	topY := highestNonZeroWord(y)
280
281	if topX < 0 || topY < 0 {
282		return res
283	}
284
285	lenX := topX + 1
286	lenY := topY + 1
287
288	for i := 0; i < lenX; i++ {
289		xi := x[i]
290		if xi == 0 {
291			continue
292		}
293		var carry uint64
294		k := i
295		for j := 0; j < lenY; j++ {
296			hi, lo := bits.Mul64(xi, y[j])
297			lo, c := bits.Add64(lo, res[k], 0)
298			hi += c
299			lo, c = bits.Add64(lo, carry, 0)
300			hi += c
301			res[k] = lo
302			carry = hi
303			k++
304		}
305		res[i+lenY] = carry
306	}
307
308	return res
309}
310
311// highestNonZeroWord returns the highest index with non-zero value or -1 if the Uint is zero.
312func highestNonZeroWord(u *Uint) int {
313	for i := 3; i >= 0; i-- {
314		if u[i] != 0 {
315			return i
316		}
317	}
318	return -1
319}
320
321// umulStep computes (hi * 2^64 + lo) = z + (x * y) + carry.
322func umulStep(z, x, y, carry uint64) (hi, lo uint64) {
323	hi, lo = bits.Mul64(x, y)
324	lo, carry = bits.Add64(lo, carry, 0)
325	hi += carry
326	lo, carry = bits.Add64(lo, z, 0)
327	hi += carry
328	return hi, lo
329}
330
331// umulHop computes (hi * 2^64 + lo) = z + (x * y)
332func umulHop(z, x, y uint64) (hi, lo uint64) {
333	hi, lo = bits.Mul64(x, y)
334	lo, carry := bits.Add64(lo, z, 0)
335	hi += carry
336	return hi, lo
337}
338
339// udivremBy1 divides u by single normalized word d and produces both quotient and remainder.
340// The quotient is stored in provided quot.
341func udivremBy1(quot, u []uint64, d uint64) (rem uint64) {
342	reciprocal := reciprocal2by1(d)
343	rem = u[len(u)-1] // Set the top word as remainder.
344	for j := len(u) - 2; j >= 0; j-- {
345		quot[j], rem = udivrem2by1(rem, u[j], d, reciprocal)
346	}
347	return rem
348}
349
350// udivremKnuth implements the division of u by normalized multiple word d from the Knuth's division algorithm.
351// The quotient is stored in provided quot - len(u)-len(d) words.
352// Updates u to contain the remainder - len(d) words.
353func udivremKnuth(quot, u, d []uint64) {
354	dLen := len(d)
355	dh := d[dLen-1]
356	dl := d[dLen-2]
357	reciprocal := reciprocal2by1(dh)
358
359	for j := len(u) - dLen - 1; j >= 0; j-- {
360		u2 := u[j+dLen]
361		u1 := u[j+dLen-1]
362		u0 := u[j+dLen-2]
363
364		var qhat, rhat uint64
365		if u2 >= dh { // Division overflows.
366			qhat = 18446744073709551615 // max uint64
367			// NOTE: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case).
368		} else {
369			qhat, rhat = udivrem2by1(u2, u1, dh, reciprocal)
370			ph, pl := bits.Mul64(qhat, dl)
371			if ph > rhat || (ph == rhat && pl > u0) {
372				qhat--
373				// NOTE: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case).
374			}
375		}
376
377		// Multiply and subtract.
378		borrow := subMulTo(u[j:], d, qhat)
379		u[j+dLen] = u2 - borrow
380		if u2 < borrow { // Too much subtracted, add back.
381			qhat--
382			u[j+dLen] += addTo(u[j:], d)
383		}
384
385		quot[j] = qhat // Store quotient digit.
386	}
387}
388
389// isBitSet returns true if bit n-th is set, where n = 0 is LSB.
390// The n must be <= 255.
391func (z *Uint) isBitSet(n uint) bool {
392	return (z[n/64] & (1 << (n % 64))) != 0
393}
394
395func (z *Uint) IsOverflow() bool {
396	return z.isBitSet(255)
397}
398
399// addTo computes x += y.
400// Requires len(x) >= len(y).
401func addTo(x, y []uint64) uint64 {
402	var carry uint64
403	for i := 0; i < len(y); i++ {
404		x[i], carry = bits.Add64(x[i], y[i], carry)
405	}
406	return carry
407}
408
409// subMulTo computes x -= y * multiplier.
410// Requires len(x) >= len(y).
411func subMulTo(x, y []uint64, multiplier uint64) uint64 {
412	var borrow uint64
413	for i := 0; i < len(y); i++ {
414		s, carry1 := bits.Sub64(x[i], borrow, 0)
415		ph, pl := bits.Mul64(y[i], multiplier)
416		t, carry2 := bits.Sub64(s, pl, 0)
417		x[i] = t
418		borrow = ph + carry1 + carry2
419	}
420	return borrow
421}
422
423// reciprocal2by1 computes <^d, ^0> / d.
424func reciprocal2by1(d uint64) uint64 {
425	reciprocal, _ := bits.Div64(^d, 18446744073709551615, d)
426	return reciprocal
427}
428
429// udivrem2by1 divides <uh, ul> / d and produces both quotient and remainder.
430// It uses the provided d's reciprocal.
431// Implementation ported from https://github.com/chfast/intx and is based on
432// "Improved division by invariant integers", Algorithm 4.
433func udivrem2by1(uh, ul, d, reciprocal uint64) (quot, rem uint64) {
434	qh, ql := bits.Mul64(reciprocal, uh)
435	ql, carry := bits.Add64(ql, ul, 0)
436	qh, _ = bits.Add64(qh, uh, carry)
437	qh++
438
439	r := ul - qh*d
440
441	if r > ql {
442		qh--
443		r += d
444	}
445
446	if r >= d {
447		qh++
448		r -= d
449	}
450
451	return qh, r
452}