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

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