Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
notJoon committed Sep 27, 2024
1 parent c115c83 commit 2ced198
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 67 deletions.
103 changes: 45 additions & 58 deletions examples/gno.land/p/demo/int256/arithmetic.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,47 @@ import (

var divisionByZeroError = "division by zero"

// Add adds two int256 values and saves the result in z.
func (z *Int) Add(x, y *Int) *Int {
z.value.Add(&x.value, &y.value)
return z
}

// AddUint256 adds int256 and uint256 values and saves the result in z.
func (z *Int) AddUint256(x *Int, y *uint256.Uint) *Int {
z.value.Add(&x.value, y)
return z
}

// Sub subtracts two int256 values and saves the result in z.
func (z *Int) Sub(x, y *Int) *Int {
z.value.Sub(&x.value, &y.value)
return z
}

// SubUint256 subtracts uint256 and int256 values and saves the result in z.
func (z *Int) SubUint256(x *Int, y *uint256.Uint) *Int {
z.value.Sub(&x.value, y)
return z
}

// Mul multiplies two int256 values and saves the result in z.
//
// It considers the signs of the operands to determine the sign of the result.
func (z *Int) Mul(x, y *Int) *Int {
z.value.Mul(&x.value, &y.value)
xAbs, xSign := x.Abs(), x.Sign()
yAbs, ySign := y.Abs(), y.Sign()

z.value.Mul(xAbs, yAbs)

if xSign != ySign {
z.value.Neg(&z.value)
}

return z
}

// Abs returns the absolute value of z.
func (z *Int) Abs() *uint256.Uint {
var absValue uint256.Uint
if z.Sign() >= 0 {
Expand Down Expand Up @@ -131,7 +147,7 @@ func (z *Int) Div(x, y *Int) *Int {
xAbs, xSign := x.Abs(), x.Sign()
yAbs, ySign := y.Abs(), y.Sign()

// perform unsigned division on the absolute values
// Step 4: Perform unsigned division on the absolute values
z.value.Div(xAbs, yAbs)

// Step 5: Adjust the sign of the result
Expand All @@ -143,17 +159,6 @@ func (z *Int) Div(x, y *Int) *Int {
return z
}

// Quo sets z to the quotient x/y for y != 0 and returns z.
// It implements truncated division (or T-division).
//
// The function performs the following steps:
// 1. Check for division by zero
// 2. Determine the signs of x and y
// 3. Calculate the absolute values of x and y
// 4. Perform unsigned division and get the remainder
// 5. Adjust the quotient for truncation towards zero
// 6. Adjust the sign of the result
//
// Example visualization for 8-bit integers (scaled down from 256-bit for simplicity):
//
// Let x = -7 (11111001 in two's complement) and y = 3 (00000011)
Expand All @@ -173,24 +178,17 @@ func (z *Int) Div(x, y *Int) *Int {
//
// Step 4: Unsigned division
//
// 7 / 3 = 2 remainder 1
// q = 2: 00000010
// r = 1: 00000001
//
// Step 5: Adjust for truncation
//
// Signs are different and r != 0, so add 1 to q
// q = 3: 00000011
// 7 / 3 = 2: 00000010
//
// Step 6: Adjust sign (x and y have different signs)
// Step 5: Adjust sign (x and y have different signs)
//
// -3: 00000011 -> 11111101
// NOT: 11111100
// +1: 11111101
// -2: 00000010 -> 11111110
// NOT: 11111101
// +1: 11111110
//
// Final result: -3 (11111101 in two's complement)
// Final result: -2 (11111110 in two's complement)
//
// Note: This implementation ensures correct truncation towards zero for negative dividends.
// Note: This implementation rounds towards zero, as is standard in Go.
func (z *Int) Quo(x, y *Int) *Int {
// Step 1: Check for division by zero
if y.IsZero() {
Expand All @@ -201,32 +199,15 @@ func (z *Int) Quo(x, y *Int) *Int {
xAbs, xSign := x.Abs(), x.Sign()
yAbs, ySign := y.Abs(), y.Sign()

// Step 4: Perform unsigned division and get the remainder
//
// q = xAbs / yAbs
// r = xAbs % yAbs
//
// Use Euclidean division to always yields a non-negative remainder, which is
// crucial for correct truncation towards zero in subsequent steps.
// By using this method here, we can easily adjust the quotient in _Step 5_
// to achieve truncated division semantics.
var q, r uint256.Uint
q.DivMod(xAbs, yAbs, &r)

// Step 5: Adjust the quotient for truncation towards zero.
//
// If x and y have different signs and there's a non-zero remainder,
// we need to round towards zero by adding 1 to the quotient magnitude.
if (xSign < 0) != (ySign < 0) && !r.IsZero() {
q.Add(&q, uint1)
}
// perform unsigned division on the absolute values
z.value.Div(xAbs, yAbs)

// Step 6: Adjust the sign of the result
// Step 5: Adjust the sign of the result
// if x and y have different signs, the result must be negative
if xSign != ySign {
q.Neg(&q)
z.value.Neg(&z.value)
}

z.value.Set(&q)
return z
}

Expand Down Expand Up @@ -311,20 +292,22 @@ func (z *Int) DivE(x, y *Int) *Int {
panic(divisionByZeroError)
}

z.Div(x, y)
// Compute the truncated division quotient
z.Quo(x, y)

// Get the remainder using T-division
// Compute the remainder
r := new(Int).Rem(x, y)

// Adjust the quotient if necessary
if r.Sign() >= 0 {
return z
}
if y.Sign() > 0 {
return z.Sub(z, int1)
// If the remainder is negative, adjust the quotient
if r.Sign() < 0 {
if y.Sign() > 0 {
z.Sub(z, NewInt(1))
} else {
z.Add(z, NewInt(1))
}
}

return z.Add(z, int1)
return z
}

// ModE computes the Euclidean modulus of x by y, setting z to the result and returning z.
Expand Down Expand Up @@ -383,6 +366,8 @@ func (z *Int) ModE(x, y *Int) *Int {
}

// Sets z to the sum x + y, where z and x are uint256s and y is an int256.
//
// If the y is positive, it adds y.value to x. otherwise, it subtracts y.Abs() from x.
func AddDelta(z, x *uint256.Uint, y *Int) {
if y.Sign() >= 0 {
z.Add(x, &y.value)
Expand All @@ -392,6 +377,8 @@ func AddDelta(z, x *uint256.Uint, y *Int) {
}

// Sets z to the sum x + y, where z and x are uint256s and y is an int256.
//
// This function returns true if the addition overflows, false otherwise.
func AddDeltaOverflow(z, x *uint256.Uint, y *Int) bool {
var overflow bool
if y.Sign() >= 0 {
Expand Down
5 changes: 4 additions & 1 deletion examples/gno.land/p/demo/int256/arithmetic_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ func TestMul(t *testing.T) {
{"5", "-3", "-15"},
{"0", "3", "0"},
{"3", "0", "0"},
{"-5", "-3", "15"},
{"115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "115792089237316195423570985008687907853269984665640564039457584007913129639935"},
}

for _, tc := range tests {
Expand Down Expand Up @@ -358,7 +360,8 @@ func TestQuo(t *testing.T) {
{"10", "-1", "-10"},
{"-10", "1", "-10"},
{"-10", "-1", "10"},
// {"10", "-3", "-3"},
{"10", "-3", "-3"},
{"-10", "3", "-3"},
{"10", "3", "3"},
}

Expand Down
23 changes: 23 additions & 0 deletions examples/gno.land/p/demo/int256/bitwise.gno
Original file line number Diff line number Diff line change
@@ -1,30 +1,53 @@
package int256

// Not sets z to the bitwise NOT of x and returns z.
//
// The bitwise NOT operation flips each bit of the operand.
func (z *Int) Not(x *Int) *Int {
z.value.Not(&x.value)
return z
}

// And sets z to the bitwise AND of x and y and returns z.
//
// The bitwise AND operation results in a value that has a bit set
// only if both corresponding bits of the operands are set.
func (z *Int) And(x, y *Int) *Int {
z.value.And(&x.value, &y.value)
return z
}

// Or sets z to the bitwise OR of x and y and returns z.
//
// The bitwise OR operation results in a value that has a bit set
// if at least one of the corresponding bits of the operands is set.
func (z *Int) Or(x, y *Int) *Int {
z.value.Or(&x.value, &y.value)
return z
}

// Xor sets z to the bitwise XOR of x and y and returns z.
//
// The bitwise XOR operation results in a value that has a bit set
// only if the corresponding bits of the operands are different.
func (z *Int) Xor(x, y *Int) *Int {
z.value.Xor(&x.value, &y.value)
return z
}

// Rsh sets z to the result of right-shifting x by n bits and returns z.
//
// Right shift operation moves all bits in the operand to the right by the specified number of positions.
// Bits shifted out on the right are discarded, and zeros are shifted in on the left.
func (z *Int) Rsh(x *Int, n uint) *Int {
z.value.Rsh(&x.value, n)
return z
}

// Lsh sets z to the result of left-shifting x by n bits and returns z.
//
// Left shift operation moves all bits in the operand to the left by the specified number of positions.
// Bits shifted out on the left are discarded, and zeros are shifted in on the right.
func (z *Int) Lsh(x *Int, n uint) *Int {
z.value.Lsh(&x.value, n)
return z
Expand Down
2 changes: 0 additions & 2 deletions examples/gno.land/p/demo/int256/cmp_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ func TestLt(t *testing.T) {
{"0", "-1", false},
{"1", "1", false},
{"-1", "-1", false},
// {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false},
}

for _, tc := range tests {
Expand Down Expand Up @@ -208,7 +207,6 @@ func TestGt(t *testing.T) {
{"0", "-1", true},
{"1", "1", false},
{"-1", "-1", false},
// {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true},
}

for _, tc := range tests {
Expand Down
6 changes: 5 additions & 1 deletion examples/gno.land/p/demo/int256/int256.gno
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ func Zero() *Int { return &Int{} }
//
// This function is convenient for operations that require a unit value,
// such as incrementing or serving as an identity element in multiplication.
func One() *Int { return int1 }
func One() *Int {
return &Int{
value: *uint256.NewUint(1),
}
}

// Sign determines the sign of the Int.
//
Expand Down
10 changes: 5 additions & 5 deletions examples/gno.land/p/demo/int256/int256_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestInitializers(t *testing.T) {
}

func TestNewInt(t *testing.T) {
testCases := []struct {
tests := []struct {
input int64
expected int
}{
Expand All @@ -43,10 +43,10 @@ func TestNewInt(t *testing.T) {
{-9223372036854775808, -1}, // min int64
}

for _, tc := range testCases {
z := NewInt(tc.input)
if z.Sign() != tc.expected {
t.Errorf("NewInt(%d) = %d, want %d", tc.input, z.Sign(), tc.expected)
for _, tt := range tests {
z := NewInt(tt.input)
if z.Sign() != tt.expected {
t.Errorf("NewInt(%d) = %d, want %d", tt.input, z.Sign(), tt.expected)
}
}
}
Expand Down

0 comments on commit 2ced198

Please sign in to comment.