From 0c53f5a1ffcb7bfdd2a70c522c7ace759de3a800 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Thu, 26 Sep 2024 14:54:08 +0900 Subject: [PATCH] bitwise --- examples/gno.land/p/demo/int256/absolute.gnoA | 2 +- .../gno.land/p/demo/int256/absolute_test.gnoA | 62 +++--- .../gno.land/p/demo/int256/arithmetic.gno | 112 ++++------ examples/gno.land/p/demo/int256/bitwise.gno | 31 +++ examples/gno.land/p/demo/int256/bitwise.gnoA | 94 --------- .../gno.land/p/demo/int256/bitwise_test.gno | 135 ++++++++++++ .../gno.land/p/demo/int256/bitwise_test.gnoA | 199 ------------------ examples/gno.land/p/demo/int256/cmp.gno | 126 +++-------- .../int256/{cmp_test.gnoA => cmp_test.gno} | 36 ++-- examples/gno.land/p/demo/int256/int256.gno | 54 ++--- .../gno.land/p/demo/int256/int256_test.gno | 159 ++++++++++---- 11 files changed, 432 insertions(+), 578 deletions(-) create mode 100644 examples/gno.land/p/demo/int256/bitwise.gno delete mode 100644 examples/gno.land/p/demo/int256/bitwise.gnoA create mode 100644 examples/gno.land/p/demo/int256/bitwise_test.gno delete mode 100644 examples/gno.land/p/demo/int256/bitwise_test.gnoA rename examples/gno.land/p/demo/int256/{cmp_test.gnoA => cmp_test.gno} (81%) diff --git a/examples/gno.land/p/demo/int256/absolute.gnoA b/examples/gno.land/p/demo/int256/absolute.gnoA index 25c71b5ab20..989d0d53029 100644 --- a/examples/gno.land/p/demo/int256/absolute.gnoA +++ b/examples/gno.land/p/demo/int256/absolute.gnoA @@ -1,6 +1,6 @@ package int256 -// import "gno.land/p/demo/uint256" +import "gno.land/p/demo/uint256" // Abs returns |z| // func (z *Int) Abs() *uint256.Uint { diff --git a/examples/gno.land/p/demo/int256/absolute_test.gnoA b/examples/gno.land/p/demo/int256/absolute_test.gnoA index 6e6a6297e9c..5c5d3ae2908 100644 --- a/examples/gno.land/p/demo/int256/absolute_test.gnoA +++ b/examples/gno.land/p/demo/int256/absolute_test.gnoA @@ -1,36 +1,36 @@ package int256 -// import ( -// "testing" - -// "gno.land/p/demo/uint256" -// ) - -// func TestAbs(t *testing.T) { -// tests := []struct { -// x, want string -// }{ -// {"0", "0"}, -// {"1", "1"}, -// {"-1", "1"}, -// {"-2", "2"}, -// {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, -// } - -// for _, tc := range tests { -// x, err := FromDecimal(tc.x) -// if err != nil { -// t.Error(err) -// continue -// } - -// got := x.Abs() - -// if got.ToString() != tc.want { -// t.Errorf("Abs(%s) = %v, want %v", tc.x, got.ToString(), tc.want) -// } -// } -// } +import ( + "testing" + + "gno.land/p/demo/uint256" +) + +func TestAbs(t *testing.T) { + tests := []struct { + x, want string + }{ + {"0", "0"}, + {"1", "1"}, + {"-1", "1"}, + {"-2", "2"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := x.Abs() + + if got.ToString() != tc.want { + t.Errorf("Abs(%s) = %v, want %v", tc.x, got.ToString(), tc.want) + } + } +} // func TestAbsGt(t *testing.T) { // tests := []struct { diff --git a/examples/gno.land/p/demo/int256/arithmetic.gno b/examples/gno.land/p/demo/int256/arithmetic.gno index 0c535b1db2b..3f8c709dc4d 100644 --- a/examples/gno.land/p/demo/int256/arithmetic.gno +++ b/examples/gno.land/p/demo/int256/arithmetic.gno @@ -1,6 +1,10 @@ package int256 -import "gno.land/p/demo/uint256" +import ( + "gno.land/p/demo/uint256" +) + +var divisionByZeroError = "division by zero" func (z *Int) Add(x, y *Int) *Int { z.value.Add(&x.value, &y.value) @@ -27,9 +31,9 @@ func (z *Int) Mul(x, y *Int) *Int { return z } -/* ---------------------------------------------------------------------------- -// Division and modulus operations -// ---------------------------------------------------------------------------- +/* +--------------------------------------------------------------------------+ +// |Division and modulus operations | +// +--------------------------------------------------------------------------+ // // This package provides three different division and modulus operations: // - Div and Rem: Truncated division (T-division) @@ -111,15 +115,11 @@ func (z *Int) Mul(x, y *Int) *Int { // Note: This implementation rounds towards zero, as is standard in Go. func (z *Int) Div(x, y *Int) *Int { // Step 1: Check for division by zero - if y.value.IsZero() { - panic("division by zero") + if y.IsZero() { + panic(divisionByZeroError) } - // Step 2: Determine the signs of x and y - xSign := x.Sign() - ySign := y.Sign() - - // Step 3: Calculate the absolute values of x and y + // Step 2, 3: Calculate the absolute values of x and y xAbs, xSign := abs(x) yAbs, ySign := abs(y) @@ -129,8 +129,7 @@ func (z *Int) Div(x, y *Int) *Int { // Step 5: Adjust the sign of the result // if x and y have different signs, the result must be negative if xSign != ySign { - // convert the positive result to negative using two's complement - setNegative(&z.value, &z.value) + z.value.Not(&z.value).Add(&z.value, uint1) } return z @@ -186,22 +185,21 @@ func (z *Int) Div(x, y *Int) *Int { // Note: This implementation ensures correct truncation towards zero for negative dividends. func (z *Int) Quo(x, y *Int) *Int { // Step 1: Check for division by zero - if y.value.IsZero() { - panic("division by zero") + if y.IsZero() { + panic(divisionByZeroError) } - // Step 2: Determine the signs of x and y - xSign := x.Sign() - ySign := y.Sign() - - // Step 3: Calculate the absolute values of x and y + // Step 2, 3: Calculate the absolute values of x and y var xAbs, yAbs, q, r uint256.Uint + var xSign, ySign int + xAbs, xSign = abs(x) yAbs, ySign = abs(y) // Step 4: Perform unsigned division and get the remainder - // q = xAbs / yAbs - // r = xAbs % yAbs + // + // 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. @@ -214,12 +212,12 @@ func (z *Int) Quo(x, y *Int) *Int { // 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, one) + q.Add(&q, uint1) } // Step 6: Adjust the sign of the result if xSign != ySign { - setNegative(&q, &q) + q.Not(&q).Add(&q, uint1) } z.value.Set(&q) @@ -269,28 +267,23 @@ func (z *Int) Quo(x, y *Int) *Int { // Note: The sign of the remainder is always the same as the sign of the dividend (x). func (z *Int) Rem(x, y *Int) *Int { // Step 1: Check for division by zero - if y.value.IsZero() { - panic("division by zero") + if y.IsZero() { + panic(divisionByZeroError) } - // Step 2: Determine the signs of x and y - xSign := x.Sign() - ySign := y.Sign() - - // Step 3: Calculate the absolute values of x and y + // Step 2, 3 var xAbs, yAbs, q, r uint256.Uint + var xSign int + xAbs, xSign = abs(x) - yAbs, ySign = abs(y) + yAbs, _ = abs(y) // Step 4: Perform unsigned division and get the remainder q.DivMod(&xAbs, &yAbs, &r) // Step 5: Adjust the sign of the remainder - if xSign < 0 { - if !r.IsZero() { - // convert positive remainder to negative - setNegative(&r, &r) - } + if xSign < 0 && !r.IsZero() { + r.Not(&r).Add(&r, uint1) } z.value.Set(&r) @@ -310,8 +303,8 @@ func (z *Int) Mod(x, y *Int) *Int { // 1. The remainder is always non-negative: 0 <= x mod y < |y| // 2. It follows the identity: x = y * (x div y) + (x mod y) func (z *Int) DivE(x, y *Int) *Int { - if y.value.IsZero() { - panic("division by zero") + if y.IsZero() { + panic(divisionByZeroError) } z.Div(x, y) @@ -320,14 +313,10 @@ func (z *Int) DivE(x, y *Int) *Int { r := new(Int).Rem(x, y) // Adjust the quotient if necessary - if r.Sign() < 0 { - if y.Sign() > 0 { - return z.Sub(z, NewInt(1)) - } - return z.Add(z, NewInt(1)) - } + if r.Sign() >= 0 { return z } + if y.Sign() > 0 { return z.Sub(z, int1) } - return z + return z.Add(z, int1) } // ModE computes the Euclidean modulus of x by y, setting z to the result and returning z. @@ -367,39 +356,30 @@ func (z *Int) DivE(x, y *Int) *Int { // Note: This implementation ensures that the result always has the same sign as y, // which is different from the Rem operation. func (z *Int) ModE(x, y *Int) *Int { - if y.value.IsZero() { - panic("division by zero") + if y.IsZero() { + panic(divisionByZeroError) } // Perform T-division to get the remainder z.Rem(x, y) // Adjust the remainder if necessary - if z.Sign() < 0 { - if y.Sign() > 0 { - return z.Add(z, y) - } - return z.Sub(z, y) - } + if z.Sign() >= 0 { return z } + if y.Sign() > 0 { return z.Add(z, y) } - return z + return z.Sub(z, y) } func abs(x *Int) (uint256.Uint, int) { sign := x.Sign() - var absVal uint256.Uint - absVal.Set(&x.value) - if sign < 0 { - setNegative(&absVal, &x.value) - sign = -1 - } else { - sign = 1 + + if sign >= 0 { + return x.value, 1 } - return absVal, sign -} -func setNegative(z, x *uint256.Uint) { - z.Set(x).Not(z).Add(z, one) + var absVal uint256.Uint + absVal.Set(&x.value).Not(&absVal).Add(&absVal, uint1) + return absVal, -1 } // not implemented yet diff --git a/examples/gno.land/p/demo/int256/bitwise.gno b/examples/gno.land/p/demo/int256/bitwise.gno new file mode 100644 index 00000000000..70f13c172ea --- /dev/null +++ b/examples/gno.land/p/demo/int256/bitwise.gno @@ -0,0 +1,31 @@ +package int256 + +func (z *Int) Not(x *Int) *Int { + z.value.Not(&x.value) + return z +} + +func (z *Int) And(x, y *Int) *Int { + z.value.And(&x.value, &y.value) + return z +} + +func (z *Int) Or(x, y *Int) *Int { + z.value.Or(&x.value, &y.value) + return z +} + +func (z *Int) Xor(x, y *Int) *Int { + z.value.Xor(&x.value, &y.value) + return z +} + +func (z *Int) Rsh(x *Int, n uint) *Int { + z.value.Rsh(&x.value, n) + return z +} + +func (z *Int) Lsh(x *Int, n uint) *Int { + z.value.Lsh(&x.value, n) + return z +} diff --git a/examples/gno.land/p/demo/int256/bitwise.gnoA b/examples/gno.land/p/demo/int256/bitwise.gnoA deleted file mode 100644 index c0d0f65f78f..00000000000 --- a/examples/gno.land/p/demo/int256/bitwise.gnoA +++ /dev/null @@ -1,94 +0,0 @@ -package int256 - -import ( - "gno.land/p/demo/uint256" -) - -// Or sets z = x | y and returns z. -func (z *Int) Or(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) | (-y) == ^(x-1) | ^(y-1) == ^((x-1) & (y-1)) == -(((x-1) & (y-1)) + 1) - x1 := new(uint256.Uint).Sub(x.abs, one) - y1 := new(uint256.Uint).Sub(y.abs, one) - z.abs = z.abs.Add(z.abs.And(x1, y1), one) - z.neg = true // z cannot be zero if x and y are negative - return z - } - - // x | y == x | y - z.abs = z.abs.Or(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - if x.neg { - x, y = y, x // | is symmetric - } - - // x | (-y) == x | ^(y-1) == ^((y-1) &^ x) == -(^((y-1) &^ x) + 1) - y1 := new(uint256.Uint).Sub(y.abs, one) - z.abs = z.abs.Add(z.abs.AndNot(y1, x.abs), one) - z.neg = true // z cannot be zero if one of x or y is negative - - return z -} - -// And sets z = x & y and returns z. -func (z *Int) And(x, y *Int) *Int { - if x.neg == y.neg { - if x.neg { - // (-x) & (-y) == ^(x-1) & ^(y-1) == ^((x-1) | (y-1)) == -(((x-1) | (y-1)) + 1) - x1 := new(uint256.Uint).Sub(x.abs, one) - y1 := new(uint256.Uint).Sub(y.abs, one) - z.abs = z.abs.Add(z.abs.Or(x1, y1), one) - z.neg = true // z cannot be zero if x and y are negative - return z - } - - // x & y == x & y - z.abs = z.abs.And(x.abs, y.abs) - z.neg = false - return z - } - - // x.neg != y.neg - // REF: https://cs.opensource.google/go/go/+/refs/tags/go1.22.1:src/math/big/int.go;l=1192-1202;drc=d57303e65f00b84b528ee682747dbe1fd3316d30 - if x.neg { - x, y = y, x // & is symmetric - } - - // x & (-y) == x & ^(y-1) == x &^ (y-1) - y1 := new(uint256.Uint).Sub(y.abs, uint256.One()) - z.abs = z.abs.AndNot(x.abs, y1) - z.neg = false - return z -} - -// Rsh sets z = x >> n and returns z. -// OBS: Different from original implementation it was using math.Big -func (z *Int) Rsh(x *Int, n uint) *Int { - if !x.neg { - z.abs.Rsh(x.abs, n) - z.neg = x.neg - return z - } - - // REF: https://cs.opensource.google/go/go/+/refs/tags/go1.22.1:src/math/big/int.go;l=1118-1126;drc=d57303e65f00b84b528ee682747dbe1fd3316d30 - t := NewInt(0).Sub(FromUint256(x.abs), NewInt(1)) - t = t.Rsh(t, n) - - _tmp := t.Add(t, NewInt(1)) - z.abs = _tmp.Abs() - z.neg = true - - return z -} - -// Lsh sets z = x << n and returns z. -func (z *Int) Lsh(x *Int, n uint) *Int { - z.abs.Lsh(x.abs, n) - z.neg = x.neg - return z -} diff --git a/examples/gno.land/p/demo/int256/bitwise_test.gno b/examples/gno.land/p/demo/int256/bitwise_test.gno new file mode 100644 index 00000000000..98de2895585 --- /dev/null +++ b/examples/gno.land/p/demo/int256/bitwise_test.gno @@ -0,0 +1,135 @@ +package int256 + +import ( + "testing" +) + +func TestBitwise_And(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"5", "1", "1"}, // 0101 & 0001 = 0001 + {"-1", "1", "1"}, // 1111 & 0001 = 0001 + {"-5", "3", "3"}, // 1111...1011 & 0000...0011 = 0000...0011 + } + + for _, tc := range tests { + x, _ := FromDecimal(tc.x) + y, _ := FromDecimal(tc.y) + want, _ := FromDecimal(tc.want) + + got := new(Int).And(x, y) + + if got.Neq(want) { + t.Errorf("And(%s, %s) = %s, want %s", x.ToString(), y.ToString(), got.ToString(), want.ToString()) + } + } +} + +func TestBitwise_Or(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"5", "1", "5"}, // 0101 | 0001 = 0101 + {"-1", "1", "-1"}, // 1111 | 0001 = 1111 + {"-5", "3", "-5"}, // 1111...1011 | 0000...0011 = 1111...1011 + } + + for _, tc := range tests { + x, _ := FromDecimal(tc.x) + y, _ := FromDecimal(tc.y) + want, _ := FromDecimal(tc.want) + + got := new(Int).Or(x, y) + + if got.Neq(want) { + t.Errorf("Or(%s, %s) = %s, want %s", x.ToString(), y.ToString(), got.ToString(), want.ToString()) + } + } +} + +func TestBitwise_Not(t *testing.T) { + tests := []struct { + x, want string + }{ + {"5", "-6"}, // 0101 -> 1111...1010 + {"-1", "0"}, // 1111...1111 -> 0000...0000 + } + + for _, tc := range tests { + x, _ := FromDecimal(tc.x) + want, _ := FromDecimal(tc.want) + + got := new(Int).Not(x) + + if got.Neq(want) { + t.Errorf("Not(%s) = %s, want %s", x.ToString(), got.ToString(), want.ToString()) + } + } +} + +func TestBitwise_Xor(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"5", "1", "4"}, // 0101 ^ 0001 = 0100 + {"-1", "1", "-2"}, // 1111...1111 ^ 0000...0001 = 1111...1110 + {"-5", "3", "-8"}, // 1111...1011 ^ 0000...0011 = 1111...1000 + } + + for _, tt := range tests { + x, _ := FromDecimal(tt.x) + y, _ := FromDecimal(tt.y) + want, _ := FromDecimal(tt.want) + + got := new(Int).Xor(x, y) + + if got.Neq(want) { + t.Errorf("Xor(%s, %s) = %s, want %s", x.ToString(), y.ToString(), got.ToString(), want.ToString()) + } + } +} + +func TestBitwise_Rsh(t *testing.T) { + tests := []struct { + x string + n uint + want string + }{ + {"5", 1, "2"}, // 0101 >> 1 = 0010 + {"42", 3, "5"}, // 00101010 >> 3 = 00000101 + } + + for _, tt := range tests { + x, _ := FromDecimal(tt.x) + want, _ := FromDecimal(tt.want) + + got := new(Int).Rsh(x, tt.n) + + if got.Neq(want) { + t.Errorf("Rsh(%s, %d) = %s, want %s", x.ToString(), tt.n, got.ToString(), want.ToString()) + } + } +} + +func TestBitwise_Lsh(t *testing.T) { + tests := []struct { + x string + n uint + want string + }{ + {"5", 2, "20"}, // 0101 << 2 = 10100 + {"42", 5, "1344"}, // 00101010 << 5 = 10101000000 + } + + for _, tt := range tests { + x, _ := FromDecimal(tt.x) + want, _ := FromDecimal(tt.want) + + got := new(Int).Lsh(x, tt.n) + + if got.Neq(want) { + t.Errorf("Lsh(%s, %d) = %s, want %s", x.ToString(), tt.n, got.ToString(), want.ToString()) + } + } +} diff --git a/examples/gno.land/p/demo/int256/bitwise_test.gnoA b/examples/gno.land/p/demo/int256/bitwise_test.gnoA deleted file mode 100644 index 8dc16cd17ac..00000000000 --- a/examples/gno.land/p/demo/int256/bitwise_test.gnoA +++ /dev/null @@ -1,199 +0,0 @@ -package int256 - -import ( - "testing" - - "gno.land/p/demo/uint256" -) - -func TestOr(t *testing.T) { - tests := []struct { - name string - x, y, want Int - }{ - { - name: "all zeroes", - x: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - y: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - }, - { - name: "all ones", - x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - y: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - want: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - }, - { - name: "mixed", - x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, neg: false}, - y: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, neg: false}, - want: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - }, - { - name: "one operand all ones", - x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - y: Int{abs: &uint256.Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, neg: false}, - want: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := New() - got.Or(&tc.x, &tc.y) - - if got.Neq(&tc.want) { - t.Errorf("Or(%v, %v) = %v, want %v", tc.x, tc.y, got, tc.want) - } - }) - } -} - -func TestAnd(t *testing.T) { - tests := []struct { - name string - x, y, want Int - }{ - { - name: "all zeroes", - x: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - y: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - }, - { - name: "all ones", - x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - y: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - want: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - }, - { - name: "mixed", - x: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - y: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - }, - { - name: "mixed 2", - x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - y: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - }, - { - name: "mixed 3", - x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, neg: false}, - y: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, neg: false}, - want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - }, - { - name: "one operand zero", - x: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - y: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - want: Int{abs: &uint256.Uint{arr: [4]uint64{0, 0, 0, 0}}, neg: false}, - }, - { - name: "one operand all ones", - x: Int{abs: &uint256.Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, neg: false}, - y: Int{abs: &uint256.Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, neg: false}, - want: Int{abs: &uint256.Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, neg: false}, - }, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - got := New() - got.And(&tc.x, &tc.y) - - if got.Neq(&tc.want) { - t.Errorf("And(%v, %v) = %v, want %v", tc.x, tc.y, got, tc.want) - } - }) - } -} - -func TestRsh(t *testing.T) { - tests := []struct { - x string - n uint - want string - }{ - {"1024", 0, "1024"}, - {"1024", 1, "512"}, - {"1024", 2, "256"}, - {"1024", 10, "1"}, - {"1024", 11, "0"}, - {"18446744073709551615", 0, "18446744073709551615"}, - {"18446744073709551615", 1, "9223372036854775807"}, - {"18446744073709551615", 62, "3"}, - {"18446744073709551615", 63, "1"}, - {"18446744073709551615", 64, "0"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 0, "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 1, "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 128, "340282366920938463463374607431768211455"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 255, "1"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", 256, "0"}, - {"-1024", 0, "-1024"}, - {"-1024", 1, "-512"}, - {"-1024", 2, "-256"}, - {"-1024", 10, "-1"}, - {"-1024", 10, "-1"}, - {"-9223372036854775808", 0, "-9223372036854775808"}, - {"-9223372036854775808", 1, "-4611686018427387904"}, - {"-9223372036854775808", 62, "-2"}, - {"-9223372036854775808", 63, "-1"}, - {"-9223372036854775808", 64, "-1"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 0, "-57896044618658097711785492504343953926634992332820282019728792003956564819968"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 1, "-28948022309329048855892746252171976963317496166410141009864396001978282409984"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 253, "-4"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 254, "-2"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 255, "-1"}, - {"-57896044618658097711785492504343953926634992332820282019728792003956564819968", 256, "-1"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.Rsh(x, tc.n) - - if got.ToString() != tc.want { - t.Errorf("Rsh(%s, %d) = %v, want %v", tc.x, tc.n, got.ToString(), tc.want) - } - } -} - -func TestLsh(t *testing.T) { - tests := []struct { - x string - n uint - want string - }{ - {"1", 0, "1"}, - {"1", 1, "2"}, - {"1", 2, "4"}, - {"2", 0, "2"}, - {"2", 1, "4"}, - {"2", 2, "8"}, - {"-2", 0, "-2"}, - {"-4", 0, "-4"}, - {"-8", 0, "-8"}, - } - - for _, tc := range tests { - x, err := FromDecimal(tc.x) - if err != nil { - t.Error(err) - continue - } - - got := New() - got.Lsh(x, tc.n) - - if got.ToString() != tc.want { - t.Errorf("Lsh(%s, %d) = %v, want %v", tc.x, tc.n, got.ToString(), tc.want) - } - } -} diff --git a/examples/gno.land/p/demo/int256/cmp.gno b/examples/gno.land/p/demo/int256/cmp.gno index 0df8462d22c..ee4e035638e 100644 --- a/examples/gno.land/p/demo/int256/cmp.gno +++ b/examples/gno.land/p/demo/int256/cmp.gno @@ -8,109 +8,53 @@ func (z *Int) Neq(x *Int) bool { return !z.Eq(x) } +// Cmp compares z and x and returns: +// +// - 1 if z > x +// - 0 if z == x +// - -1 if z < x func (z *Int) Cmp(x *Int) int { - // Check if both numbers have the same sign zSign := z.Sign() xSign := x.Sign() - if zSign != xSign { - // If signs are different, the comparison is straightforward - return zSign - xSign + if zSign == xSign { + return z.value.Cmp(&x.value) } - // If signs are the same, compare the absolute values - cmp := z.value.Cmp(&x.value) - - if zSign >= 0 { - // For positive numbers (including zero), the comparison result is correct - return cmp - } else { - // For negative numbers, invert the comparison result - return -cmp + if zSign == 0 { + return -xSign } -} -// // Eq returns true if z == x -// func (z *Int) Eq(x *Int) bool { -// return (z.neg == x.neg) && z.abs.Eq(x.abs) -// } + return zSign +} -// // Neq returns true if z != x -// func (z *Int) Neq(x *Int) bool { -// return !z.Eq(x) -// } +// IsZero returns true if z == 0 +func (z *Int) IsZero() bool { + return z.Sign() == 0 +} -// // Cmp compares x and y and returns: -// // -// // -1 if x < y -// // 0 if x == y -// // +1 if x > y -// func (z *Int) Cmp(x *Int) (r int) { -// // x cmp y == x cmp y -// // x cmp (-y) == x -// // (-x) cmp y == y -// // (-x) cmp (-y) == -(x cmp y) -// switch { -// case z == x: -// // nothing to do -// case z.neg == x.neg: -// r = z.abs.Cmp(x.abs) -// if z.neg { -// r = -r -// } -// case z.neg: -// r = -1 -// default: -// r = 1 -// } -// return -// } +// IsNeg returns true if z < 0 +func (z *Int) IsNeg() bool { + return z.Sign() < 0 +} -// // IsZero returns true if z == 0 -// func (z *Int) IsZero() bool { -// return z.abs.IsZero() -// } +func (z *Int) Lt(x *Int) bool { + return z.Cmp(x) < 0 +} -// // IsNeg returns true if z < 0 -// func (z *Int) IsNeg() bool { -// return z.neg -// } +func (z *Int) Gt(x *Int) bool { + return z.Cmp(x) > 0 +} -// // Lt returns true if z < x -// func (z *Int) Lt(x *Int) bool { -// if z.neg { -// if x.neg { -// return z.abs.Gt(x.abs) -// } else { -// return true -// } -// } else { -// if x.neg { -// return false -// } else { -// return z.abs.Lt(x.abs) -// } -// } -// } +func (z *Int) Le(x *Int) bool { + return z.Cmp(x) <= 0 +} -// // Gt returns true if z > x -// func (z *Int) Gt(x *Int) bool { -// if z.neg { -// if x.neg { -// return z.abs.Lt(x.abs) -// } else { -// return false -// } -// } else { -// if x.neg { -// return true -// } else { -// return z.abs.Gt(x.abs) -// } -// } -// } +func (z *Int) Ge(x *Int) bool { + return z.Cmp(x) >= 0 +} -// // Clone creates a new Int identical to z -// func (z *Int) Clone() *Int { -// return &Int{z.abs.Clone(), z.neg} -// } +// Clone creates a new Int identical to z +func (z *Int) Clone() *Int { + return New().FromUint256(&z.value) +} diff --git a/examples/gno.land/p/demo/int256/cmp_test.gnoA b/examples/gno.land/p/demo/int256/cmp_test.gno similarity index 81% rename from examples/gno.land/p/demo/int256/cmp_test.gnoA rename to examples/gno.land/p/demo/int256/cmp_test.gno index 81b9231babe..c646c7a7ea9 100644 --- a/examples/gno.land/p/demo/int256/cmp_test.gnoA +++ b/examples/gno.land/p/demo/int256/cmp_test.gno @@ -85,7 +85,7 @@ func TestCmp(t *testing.T) { {"-1", "0", -1}, {"0", "-1", 1}, {"1", "1", 0}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", 1}, + // {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", 1}, } for _, tc := range tests { @@ -140,7 +140,7 @@ func TestIsNeg(t *testing.T) { want bool }{ {"0", false}, - {"-0", true}, // TODO: should this be false? + {"-0", false}, {"1", false}, {"-1", true}, {"10", false}, @@ -173,7 +173,7 @@ func TestLt(t *testing.T) { {"0", "-1", false}, {"1", "1", false}, {"-1", "-1", false}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, + // {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, } for _, tc := range tests { @@ -208,7 +208,7 @@ func TestGt(t *testing.T) { {"0", "-1", true}, {"1", "1", false}, {"-1", "-1", false}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, + // {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, } for _, tc := range tests { @@ -232,21 +232,19 @@ func TestGt(t *testing.T) { } func TestClone(t *testing.T) { - tests := []struct { - x string - }{ - {"0"}, - {"-0"}, - {"1"}, - {"-1"}, - {"10"}, - {"-10"}, - {"115792089237316195423570985008687907853269984665640564039457584007913129639935"}, - {"-115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + tests := []string{ + "0", + "-0", + "1", + "-1", + "10", + "-10", + "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "-115792089237316195423570985008687907853269984665640564039457584007913129639935", } - for _, tc := range tests { - x, err := FromDecimal(tc.x) + for _, xStr := range tests { + x, err := FromDecimal(xStr) if err != nil { t.Error(err) continue @@ -254,8 +252,8 @@ func TestClone(t *testing.T) { y := x.Clone() - if x.Cmp(y) != 0 { - t.Errorf("Clone(%s) = %v, want %v", tc.x, y, x) + if x.Neq(y) { + t.Errorf("cloned value is not equal to original value") } } } diff --git a/examples/gno.land/p/demo/int256/int256.gno b/examples/gno.land/p/demo/int256/int256.gno index 6c26322ee41..008ff5233f1 100644 --- a/examples/gno.land/p/demo/int256/int256.gno +++ b/examples/gno.land/p/demo/int256/int256.gno @@ -1,10 +1,15 @@ package int256 import ( + "errors" + "gno.land/p/demo/uint256" ) -var one = uint256.NewUint(1) +var ( + int1 = NewInt(1) + uint1 = uint256.NewUint(1) +) type Int struct { value uint256.Uint @@ -33,9 +38,7 @@ func Zero() *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 { - z := &Int{} - z.value.SetUint64(1) - return z + return new(Int).SetInt64(1) } // Sign determines the sign of the Int. @@ -90,46 +93,26 @@ func MustFromDecimal(s string) *Int { // SetString sets the Int to the value represented by the input string. // This method supports decimal string representations of integers and handles // both positive and negative values. -// -// For negative numbers, it uses the two's complement representation: -// 1. Convert the absolute value to binary -// 2. Invert all bits (NOT operation) -// 3. Add 1 to the result -// -// This process ensures correct representation and arithmetic for negative numbers. func (z *Int) SetString(s string) (*Int, error) { - neg := false - if len(s) > 0 && (s[0] == '+' || s[0] == '-') { - if s[0] == '-' { - neg = true - } - // remove the sign character from the string - // and take only the numeric part + if len(s) == 0 { + return nil, errors.New("cannot set int256 from empty string") + } + + // Check for negative sign + neg := s[0] == '-' + if neg || s[0] == '+' { s = s[1:] } + // Convert string to uint256 temp, err := uint256.FromDecimal(s) if err != nil { return nil, err } - // for negative numbers, apply two's complement + // If negative, negate the uint256 value if neg { - // invert all bits (NOT operation) - temp.Not(temp) - // add one (two's complement) - temp.Add(temp, uint256.NewUint(1)) - // Example of two's complement for 8-bit number -5: - // - // Decimal: 5 - // Step 0 (binary): 0000 0101 - // Step 1 (NOT): 1111 1010 - // Step 2 (Add 1): 1111 1011 - // - // Result: 1111 1011 (binary representation of -5 in two's complement) - // - // This process scales to our 256-bit integers, allowing - // for correct representation and arithmetic of negative numbers. + temp.Neg(temp) } z.value.Set(temp) @@ -145,8 +128,7 @@ func (z *Int) SetInt64(v int64) *Int { z.value.SetUint64(uint64(v)) } else { z.value.SetUint64(uint64(-v)) - z.value.Not(&z.value) - z.value.Add(&z.value, one) + z.value.Not(&z.value).Add(&z.value, uint1) } return z } diff --git a/examples/gno.land/p/demo/int256/int256_test.gno b/examples/gno.land/p/demo/int256/int256_test.gno index 91ac1f5e461..48a4c3c7ea9 100644 --- a/examples/gno.land/p/demo/int256/int256_test.gno +++ b/examples/gno.land/p/demo/int256/int256_test.gno @@ -6,17 +6,28 @@ import ( "gno.land/p/demo/uint256" ) -func TestZero(t *testing.T) { - z := Zero() - if z.Sign() != 0 { - t.Errorf("Zero() = %d, want %d", z.Sign(), 0) +func TestInitializers(t *testing.T) { + tests := []struct { + name string + fn func() *Int + wantSign int + wantStr string + }{ + {"Zero", Zero, 0, "0"}, + {"New", New, 0, "0"}, + {"One", One, 1, "1"}, } -} -func TestNew(t *testing.T) { - z := New() - if z.Sign() != 0 { - t.Errorf("New() = %d, want %d", z.Sign(), 0) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + z := tt.fn() + if z.Sign() != tt.wantSign { + t.Errorf("%s() = %d, want %d", tt.name, z.Sign(), tt.wantSign) + } + if z.ToString() != tt.wantStr { + t.Errorf("%s() = %s, want %s", tt.name, z.ToString(), tt.wantStr) + } + }) } } @@ -41,7 +52,7 @@ func TestNewInt(t *testing.T) { } func TestFromDecimal(t *testing.T) { - testCases := []struct { + tests := []struct { input string expected int isError bool @@ -54,54 +65,68 @@ func TestFromDecimal(t *testing.T) { {"invalid", 0, true}, } - for _, tc := range testCases { - z, err := FromDecimal(tc.input) - if tc.isError { + for _, tt := range tests { + z, err := FromDecimal(tt.input) + if tt.isError { if err == nil { - t.Errorf("FromDecimal(%s) expected error, but got nil", tc.input) + t.Errorf("FromDecimal(%s) expected error, but got nil", tt.input) } } else { if err != nil { - t.Errorf("FromDecimal(%s) unexpected error: %v", tc.input, err) - } else if z.Sign() != tc.expected { - t.Errorf("FromDecimal(%s) sign is incorrect. Expected: %d, Actual: %d", tc.input, tc.expected, z.Sign()) + t.Errorf("FromDecimal(%s) unexpected error: %v", tt.input, err) + } else if z.Sign() != tt.expected { + t.Errorf("FromDecimal(%s) sign is incorrect. Expected: %d, Actual: %d", tt.input, tt.expected, z.Sign()) } } } } func TestMustFromDecimal(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Errorf("MustFromDecimal(\"invalid\") expected panic, but got nil") + tests := []struct { + input string + expected int + shouldPanic bool + }{ + {"0", 0, false}, + {"1", 1, false}, + {"-1", -1, false}, + {"123", 1, false}, + {"invalid", 0, true}, + } + + for _, tt := range tests { + if tt.shouldPanic { + defer func() { + if r := recover(); r == nil { + t.Errorf("MustFromDecimal(%q) expected panic, but got nil", tt.input) + } + }() } - }() - z := MustFromDecimal("123") - if z.Sign() != 1 { - t.Errorf("MustFromDecimal(\"123\") sign is incorrect. Expected: %d, Actual: %d", 1, z.Sign()) + z := MustFromDecimal(tt.input) + if !tt.shouldPanic && z.Sign() != tt.expected { + t.Errorf("MustFromDecimal(%q) sign is incorrect. Expected: %d, Actual: %d", tt.input, tt.expected, z.Sign()) + } } - - MustFromDecimal("invalid") } func TestSetUint64(t *testing.T) { - testCases := []uint64{ + tests := []uint64{ 0, 1, 18446744073709551615, // max uint64 } - for _, tc := range testCases { - z := New().SetUint64(tc) + for _, tt := range tests { + z := New().SetUint64(tt) if z.Sign() < 0 { - t.Errorf("SetUint64(%d) result is negative", tc) + t.Errorf("SetUint64(%d) result is negative", tt) } - if tc == 0 && z.Sign() != 0 { + if tt == 0 && z.Sign() != 0 { t.Errorf("SetUint64(0) result is not zero") } - if tc > 0 && z.Sign() != 1 { - t.Errorf("SetUint64(%d) result is not positive", tc) + if tt > 0 && z.Sign() != 1 { + t.Errorf("SetUint64(%d) result is not positive", tt) } } } @@ -116,10 +141,10 @@ func TestFromUint256(t *testing.T) { {uint256.NewUint(18446744073709551615), 1}, } - for _, tc := range tests { - z := New().FromUint256(tc.input) - if z.Sign() != tc.expected { - t.Errorf("FromUint256(%v) = %d, want %d", tc.input, z.Sign(), tc.expected) + for _, tt := range tests { + z := New().FromUint256(tt.input) + if z.Sign() != tt.expected { + t.Errorf("FromUint256(%v) = %d, want %d", tt.input, z.Sign(), tt.expected) } } } @@ -141,11 +166,63 @@ func TestSign(t *testing.T) { {"-1", -1}, } - for _, tc := range tests { - z := MustFromDecimal(tc.x) + for _, tt := range tests { + z := MustFromDecimal(tt.x) got := z.Sign() - if got != tc.want { - t.Errorf("Sign(%s) = %d, want %d", tc.x, got, tc.want) + if got != tt.want { + t.Errorf("Sign(%s) = %d, want %d", tt.x, got, tt.want) + } + } +} + +func TestSetInt64(t *testing.T) { + tests := []struct { + v int64 + expect int + }{ + {0, 0}, + {1, 1}, + {-1, -1}, + {9223372036854775807, 1}, // overflow (max int64) + {-9223372036854775808, -1}, // underflow (min int64) + } + + for _, tt := range tests { + z := New().SetInt64(tt.v) + if z.Sign() != tt.expect { + t.Errorf("SetInt64(%d) = %d, want %d", tt.v, z.Sign(), tt.expect) + } + } +} + +func TestSetAndToString(t *testing.T) { + tests := []struct { + input string + expected int + isError bool + }{ + {"0", 0, false}, + {"1", 1, false}, + {"-1", -1, false}, + {"123456789", 1, false}, + {"-123456789", -1, false}, + {"invalid", 0, true}, + } + + for _, tt := range tests { + z, err := New().SetString(tt.input) + if tt.isError { + if err == nil { + t.Errorf("SetString(%s) expected error, but got nil", tt.input) + } + } else { + if err != nil { + t.Errorf("SetString(%s) unexpected error: %v", tt.input, err) + } else if z.Sign() != tt.expected { + t.Errorf("SetString(%s) sign is incorrect. Expected: %d, Actual: %d", tt.input, tt.expected, z.Sign()) + } else if z.ToString() != tt.input { + t.Errorf("SetString(%s) string representation is incorrect. Expected: %s, Actual: %s", tt.input, tt.input, z.ToString()) + } } } }