arithmetic.gno
8.40 Kb ยท 350 lines
1package int256
2
3import (
4 "gno.land/p/demo/uint256"
5)
6
7const divisionByZeroError = "division by zero"
8
9// Add adds two int256 values and saves the result in z.
10func (z *Int) Add(x, y *Int) *Int {
11 z.value.Add(&x.value, &y.value)
12 return z
13}
14
15// AddUint256 adds int256 and uint256 values and saves the result in z.
16func (z *Int) AddUint256(x *Int, y *uint256.Uint) *Int {
17 z.value.Add(&x.value, y)
18 return z
19}
20
21// Sub subtracts two int256 values and saves the result in z.
22func (z *Int) Sub(x, y *Int) *Int {
23 z.value.Sub(&x.value, &y.value)
24 return z
25}
26
27// SubUint256 subtracts uint256 and int256 values and saves the result in z.
28func (z *Int) SubUint256(x *Int, y *uint256.Uint) *Int {
29 z.value.Sub(&x.value, y)
30 return z
31}
32
33// Mul multiplies two int256 values and saves the result in z.
34//
35// It considers the signs of the operands to determine the sign of the result.
36func (z *Int) Mul(x, y *Int) *Int {
37 xAbs, xSign := x.Abs(), x.Sign()
38 yAbs, ySign := y.Abs(), y.Sign()
39
40 z.value.Mul(xAbs, yAbs)
41
42 if xSign != ySign {
43 z.value.Neg(&z.value)
44 }
45
46 return z
47}
48
49// Abs returns the absolute value of z.
50func (z *Int) Abs() *uint256.Uint {
51 if z.Sign() >= 0 {
52 return &z.value
53 }
54
55 var absValue uint256.Uint
56 absValue.Sub(uint0, &z.value).Neg(&z.value)
57
58 return &absValue
59}
60
61// Div performs integer division z = x / y and returns z.
62// If y == 0, it panics with a "division by zero" error.
63//
64// This function handles signed division using two's complement representation:
65// 1. Determine the sign of the quotient based on the signs of x and y.
66// 2. Perform unsigned division on the absolute values.
67// 3. Adjust the result's sign if necessary.
68//
69// Example visualization for 8-bit integers (scaled down from 256-bit for simplicity):
70//
71// Let x = -6 (11111010 in two's complement) and y = 3 (00000011)
72//
73// Step 2: Determine signs
74//
75// x: negative (MSB is 1)
76// y: positive (MSB is 0)
77//
78// Step 3: Calculate absolute values
79//
80// |x| = 6: 11111010 -> 00000110
81// NOT: 00000101
82// +1: 00000110
83//
84// |y| = 3: 00000011 (already positive)
85//
86// Step 4: Unsigned division
87//
88// 6 / 3 = 2: 00000010
89//
90// Step 5: Adjust sign (x and y have different signs)
91//
92// -2: 00000010 -> 11111110
93// NOT: 11111101
94// +1: 11111110
95//
96// Note: This implementation rounds towards zero, as is standard in Go.
97func (z *Int) Div(x, y *Int) *Int {
98 // Step 1: Check for division by zero
99 if y.IsZero() {
100 panic(divisionByZeroError)
101 }
102
103 // Step 2, 3: Calculate the absolute values of x and y
104 xAbs, xSign := x.Abs(), x.Sign()
105 yAbs, ySign := y.Abs(), y.Sign()
106
107 // Step 4: Perform unsigned division on the absolute values
108 z.value.Div(xAbs, yAbs)
109
110 // Step 5: Adjust the sign of the result
111 // if x and y have different signs, the result must be negative
112 if xSign != ySign {
113 z.value.Neg(&z.value)
114 }
115
116 return z
117}
118
119// Example visualization for 8-bit integers (scaled down from 256-bit for simplicity):
120//
121// Let x = -7 (11111001 in two's complement) and y = 3 (00000011)
122//
123// Step 2: Determine signs
124//
125// x: negative (MSB is 1)
126// y: positive (MSB is 0)
127//
128// Step 3: Calculate absolute values
129//
130// |x| = 7: 11111001 -> 00000111
131// NOT: 00000110
132// +1: 00000111
133//
134// |y| = 3: 00000011 (already positive)
135//
136// Step 4: Unsigned division
137//
138// 7 / 3 = 2: 00000010
139//
140// Step 5: Adjust sign (x and y have different signs)
141//
142// -2: 00000010 -> 11111110
143// NOT: 11111101
144// +1: 11111110
145//
146// Final result: -2 (11111110 in two's complement)
147//
148// Note: This implementation rounds towards zero, as is standard in Go.
149func (z *Int) Quo(x, y *Int) *Int {
150 // Step 1: Check for division by zero
151 if y.IsZero() {
152 panic(divisionByZeroError)
153 }
154
155 // Step 2, 3: Calculate the absolute values of x and y
156 xAbs, xSign := x.Abs(), x.Sign()
157 yAbs, ySign := y.Abs(), y.Sign()
158
159 // perform unsigned division on the absolute values
160 z.value.Div(xAbs, yAbs)
161
162 // Step 5: Adjust the sign of the result
163 // if x and y have different signs, the result must be negative
164 if xSign != ySign {
165 z.value.Neg(&z.value)
166 }
167
168 return z
169}
170
171// Rem sets z to the remainder x%y for y != 0 and returns z.
172//
173// The function performs the following steps:
174// 1. Check for division by zero
175// 2. Determine the signs of x and y
176// 3. Calculate the absolute values of x and y
177// 4. Perform unsigned division and get the remainder
178// 5. Adjust the sign of the remainder
179//
180// Example visualization for 8-bit integers (scaled down from 256-bit for simplicity):
181//
182// Let x = -7 (11111001 in two's complement) and y = 3 (00000011)
183//
184// Step 2: Determine signs
185//
186// x: negative (MSB is 1)
187// y: positive (MSB is 0)
188//
189// Step 3: Calculate absolute values
190//
191// |x| = 7: 11111001 -> 00000111
192// NOT: 00000110
193// +1: 00000111
194//
195// |y| = 3: 00000011 (already positive)
196//
197// Step 4: Unsigned division
198//
199// 7 / 3 = 2 remainder 1
200// q = 2: 00000010 (not used in result)
201// r = 1: 00000001
202//
203// Step 5: Adjust sign of remainder (x is negative)
204//
205// -1: 00000001 -> 11111111
206// NOT: 11111110
207// +1: 11111111
208//
209// Final result: -1 (11111111 in two's complement)
210//
211// Note: The sign of the remainder is always the same as the sign of the dividend (x).
212func (z *Int) Rem(x, y *Int) *Int {
213 // Step 1: Check for division by zero
214 if y.IsZero() {
215 panic(divisionByZeroError)
216 }
217
218 // Step 2, 3
219 xAbs, xSign := x.Abs(), x.Sign()
220 yAbs := y.Abs()
221
222 // Step 4: Perform unsigned division and get the remainder
223 var q, r uint256.Uint
224 q.DivMod(xAbs, yAbs, &r)
225
226 // Step 5: Adjust the sign of the remainder
227 if xSign < 0 {
228 r.Neg(&r)
229 }
230
231 z.value.Set(&r)
232 return z
233}
234
235// Mod sets z to the modulus x%y for y != 0 and returns z.
236// The result (z) has the same sign as the divisor y.
237func (z *Int) Mod(x, y *Int) *Int {
238 return z.ModE(x, y)
239}
240
241// DivE performs Euclidean division of x by y, setting z to the quotient and returning z.
242// If y == 0, it panics with a "division by zero" error.
243//
244// Euclidean division satisfies the following properties:
245// 1. The remainder is always non-negative: 0 <= x mod y < |y|
246// 2. It follows the identity: x = y * (x div y) + (x mod y)
247func (z *Int) DivE(x, y *Int) *Int {
248 if y.IsZero() {
249 panic(divisionByZeroError)
250 }
251
252 // Compute the truncated division quotient
253 z.Quo(x, y)
254
255 // Compute the remainder
256 r := new(Int).Rem(x, y)
257
258 // If the remainder is negative, adjust the quotient
259 if r.Sign() < 0 {
260 if y.Sign() > 0 {
261 z.Sub(z, NewInt(1))
262 } else {
263 z.Add(z, NewInt(1))
264 }
265 }
266
267 return z
268}
269
270// ModE computes the Euclidean modulus of x by y, setting z to the result and returning z.
271// If y == 0, it panics with a "division by zero" error.
272//
273// The Euclidean modulus is always non-negative and satisfies:
274//
275// 0 <= x mod y < |y|
276//
277// Example visualization for 8-bit integers (scaled down from 256-bit for simplicity):
278//
279// Case 1: Let x = -7 (11111001 in two's complement) and y = 3 (00000011)
280//
281// Step 1: Compute remainder (using Rem)
282//
283// Result of Rem: -1 (11111111 in two's complement)
284//
285// Step 2: Adjust sign (result is negative, y is positive)
286//
287// -1 + 3 = 2
288// 11111111 + 00000011 = 00000010
289//
290// Final result: 2 (00000010)
291//
292// Case 2: Let x = -7 (11111001 in two's complement) and y = -3 (11111101 in two's complement)
293//
294// Step 1: Compute remainder (using Rem)
295//
296// Result of Rem: -1 (11111111 in two's complement)
297//
298// Step 2: Adjust sign (result is negative, y is negative)
299//
300// No adjustment needed
301//
302// Final result: -1 (11111111 in two's complement)
303//
304// Note: This implementation ensures that the result always has the same sign as y,
305// which is different from the Rem operation.
306func (z *Int) ModE(x, y *Int) *Int {
307 if y.IsZero() {
308 panic(divisionByZeroError)
309 }
310
311 // Perform T-division to get the remainder
312 z.Rem(x, y)
313
314 // Adjust the remainder if necessary
315 if z.Sign() >= 0 {
316 return z
317 }
318 if y.Sign() > 0 {
319 return z.Add(z, y)
320 }
321
322 return z.Sub(z, y)
323}
324
325// Sets z to the sum x + y, where z and x are uint256s and y is an int256.
326//
327// If the y is positive, it adds y.value to x. otherwise, it subtracts y.Abs() from x.
328func AddDelta(z, x *uint256.Uint, y *Int) {
329 if y.Sign() >= 0 {
330 z.Add(x, &y.value)
331 } else {
332 z.Sub(x, y.Abs())
333 }
334}
335
336// Sets z to the sum x + y, where z and x are uint256s and y is an int256.
337//
338// This function returns true if the addition overflows, false otherwise.
339func AddDeltaOverflow(z, x *uint256.Uint, y *Int) bool {
340 var overflow bool
341 if y.Sign() >= 0 {
342 _, overflow = z.AddOverflow(x, &y.value)
343 } else {
344 var absY uint256.Uint
345 absY.Sub(uint0, &y.value) // absY = -y.value
346 _, overflow = z.SubOverflow(x, &absY)
347 }
348
349 return overflow
350}