diff --git a/examples/gno.land/p/demo/int256/LICENSE b/examples/gno.land/p/demo/int256/LICENSE new file mode 100644 index 00000000000..fc7e78a4875 --- /dev/null +++ b/examples/gno.land/p/demo/int256/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Trịnh Đức Bảo Linh(Kevin) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/examples/gno.land/p/demo/int256/README.md b/examples/gno.land/p/demo/int256/README.md new file mode 100644 index 00000000000..be467471199 --- /dev/null +++ b/examples/gno.land/p/demo/int256/README.md @@ -0,0 +1,6 @@ +# Fixed size signed 256-bit math library + +1. This is a library specialized at replacing the big.Int library for math based on signed 256-bit types. +2. It uses [uint256](https://github.com/gnolang/gno/tree/master/examples/gno.land/p/demo/uint256) as the underlying type. + +ported from [mempooler/int256](https://github.com/mempooler/int256) diff --git a/examples/gno.land/p/demo/int256/absolute.gno b/examples/gno.land/p/demo/int256/absolute.gno new file mode 100644 index 00000000000..23beb4071a4 --- /dev/null +++ b/examples/gno.land/p/demo/int256/absolute.gno @@ -0,0 +1,20 @@ +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +// Abs returns |z| +func (z *Int) Abs() *uint256.Uint { + return z.abs.Clone() +} + +// AbsGt returns true if |z| > x, where x is a uint256 +func (z *Int) AbsGt(x *uint256.Uint) bool { + return z.abs.Gt(x) +} + +// AbsLt returns true if |z| < x, where x is a uint256 +func (z *Int) AbsLt(x *uint256.Uint) bool { + return z.abs.Lt(x) +} diff --git a/examples/gno.land/p/demo/int256/absolute_test.gno b/examples/gno.land/p/demo/int256/absolute_test.gno new file mode 100644 index 00000000000..55f6e41d0c8 --- /dev/null +++ b/examples/gno.land/p/demo/int256/absolute_test.gno @@ -0,0 +1,105 @@ +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) + } + } +} + +func TestAbsGt(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "0", "false"}, + {"1", "0", "true"}, + {"-1", "0", "true"}, + {"-1", "1", "false"}, + {"-2", "1", "true"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "0", "true"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "true"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "false"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.AbsGt(y) + + if got != (tc.want == "true") { + t.Errorf("AbsGt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestAbsLt(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "0", "false"}, + {"1", "0", "false"}, + {"-1", "0", "false"}, + {"-1", "1", "false"}, + {"-2", "1", "false"}, + {"-5", "10", "true"}, + {"31330", "31337", "true"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "0", "false"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "false"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "false"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.AbsLt(y) + + if got != (tc.want == "true") { + t.Errorf("AbsLt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} diff --git a/examples/gno.land/p/demo/int256/arithmetic.gno b/examples/gno.land/p/demo/int256/arithmetic.gno new file mode 100644 index 00000000000..ccb494bec9a --- /dev/null +++ b/examples/gno.land/p/demo/int256/arithmetic.gno @@ -0,0 +1,198 @@ +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +func (z *Int) Add(x, y *Int) *Int { + z.initiateAbs() + + neg := x.neg + + if x.neg == y.neg { + // x + y == x + y + // (-x) + (-y) == -(x + y) + z.abs = z.abs.Add(x.abs, y.abs) + } else { + // x + (-y) == x - y == -(y - x) + // (-x) + y == y - x == -(x - y) + if x.abs.Cmp(y.abs) >= 0 { + z.abs = z.abs.Sub(x.abs, y.abs) + } else { + neg = !neg + z.abs = z.abs.Sub(y.abs, x.abs) + } + } + z.neg = neg // 0 has no sign + return z +} + +// AddUint256 set z to the sum x + y, where y is a uint256, and returns z +func (z *Int) AddUint256(x *Int, y *uint256.Uint) *Int { + if x.neg { + if x.abs.Gt(y) { + z.abs.Sub(x.abs, y) + z.neg = true + } else { + z.abs.Sub(y, x.abs) + z.neg = false + } + } else { + z.abs.Add(x.abs, y) + z.neg = false + } + return z +} + +// Sets z to the sum x + y, where z and x are uint256s and y is an int256. +func AddDelta(z, x *uint256.Uint, y *Int) { + if y.neg { + z.Sub(x, y.abs) + } else { + z.Add(x, y.abs) + } +} + +// Sets z to the sum x + y, where z and x are uint256s and y is an int256. +func AddDeltaOverflow(z, x *uint256.Uint, y *Int) bool { + var overflow bool + if y.neg { + _, overflow = z.SubOverflow(x, y.abs) + } else { + _, overflow = z.AddOverflow(x, y.abs) + } + return overflow +} + +// Sub sets z to the difference x-y and returns z. +func (z *Int) Sub(x, y *Int) *Int { + z.initiateAbs() + + neg := x.neg + if x.neg != y.neg { + // x - (-y) == x + y + // (-x) - y == -(x + y) + z.abs = z.abs.Add(x.abs, y.abs) + } else { + // x - y == x - y == -(y - x) + // (-x) - (-y) == y - x == -(x - y) + if x.abs.Cmp(y.abs) >= 0 { + z.abs = z.abs.Sub(x.abs, y.abs) + } else { + neg = !neg + z.abs = z.abs.Sub(y.abs, x.abs) + } + } + z.neg = neg // 0 has no sign + return z +} + +// SubUint256 set z to the difference x - y, where y is a uint256, and returns z +func (z *Int) SubUint256(x *Int, y *uint256.Uint) *Int { + if x.neg { + z.abs.Add(x.abs, y) + z.neg = true + } else { + if x.abs.Lt(y) { + z.abs.Sub(y, x.abs) + z.neg = true + } else { + z.abs.Sub(x.abs, y) + z.neg = false + } + } + return z +} + +// Mul sets z to the product x*y and returns z. +func (z *Int) Mul(x, y *Int) *Int { + z.initiateAbs() + + z.abs = z.abs.Mul(x.abs, y.abs) + z.neg = x.neg != y.neg // 0 has no sign + return z +} + +// MulUint256 sets z to the product x*y, where y is a uint256, and returns z +func (z *Int) MulUint256(x *Int, y *uint256.Uint) *Int { + z.abs.Mul(x.abs, y) + if z.abs.IsZero() { + z.neg = false + } else { + z.neg = x.neg + } + return z +} + +// Div sets z to the quotient x/y for y != 0 and returns z. +func (z *Int) Div(x, y *Int) *Int { + z.initiateAbs() + + z.abs.Div(x.abs, y.abs) + if x.neg == y.neg { + z.neg = false + } else { + z.neg = true + } + return z +} + +// DivUint256 sets z to the quotient x/y, where y is a uint256, and returns z +// If y == 0, z is set to 0 +func (z *Int) DivUint256(x *Int, y *uint256.Uint) *Int { + z.abs.Div(x.abs, y) + if z.abs.IsZero() { + z.neg = false + } else { + z.neg = x.neg + } + return z +} + +// Quo sets z to the quotient x/y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// OBS: differs from mempooler int256, we need to panic manually if y == 0 +// Quo implements truncated division (like Go); see QuoRem for more details. +func (z *Int) Quo(x, y *Int) *Int { + if y.IsZero() { + panic("division by zero") + } + + z.initiateAbs() + + z.abs = z.abs.Div(x.abs, y.abs) + z.neg = !(z.abs.IsZero()) && x.neg != y.neg // 0 has no sign + return z +} + +// Rem sets z to the remainder x%y for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// OBS: differs from mempooler int256, we need to panic manually if y == 0 +// Rem implements truncated modulus (like Go); see QuoRem for more details. +func (z *Int) Rem(x, y *Int) *Int { + if y.IsZero() { + panic("division by zero") + } + + z.initiateAbs() + + z.abs.Mod(x.abs, y.abs) + z.neg = z.abs.Sign() > 0 && x.neg // 0 has no sign + return z +} + +// Mod sets z to the modulus x%y for y != 0 and returns z. +// If y == 0, z is set to 0 (OBS: differs from the big.Int) +func (z *Int) Mod(x, y *Int) *Int { + if x.neg { + z.abs.Div(x.abs, y.abs) + z.abs.Add(z.abs, one) + z.abs.Mul(z.abs, y.abs) + z.abs.Sub(z.abs, x.abs) + z.abs.Mod(z.abs, y.abs) + } else { + z.abs.Mod(x.abs, y.abs) + } + z.neg = false + return z +} diff --git a/examples/gno.land/p/demo/int256/arithmetic_test.gno b/examples/gno.land/p/demo/int256/arithmetic_test.gno new file mode 100644 index 00000000000..c4aeb18e3c5 --- /dev/null +++ b/examples/gno.land/p/demo/int256/arithmetic_test.gno @@ -0,0 +1,571 @@ +package int256 + +import ( + "testing" + + "gno.land/p/demo/uint256" +) + +func TestAdd(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "1"}, + {"1", "0", "1"}, + {"1", "1", "2"}, + {"1", "2", "3"}, + // NEGATIVE + {"-1", "1", "-0"}, // TODO: remove negative sign for 0 ?? + {"1", "-1", "0"}, + {"-1", "-1", "-2"}, + {"-1", "-2", "-3"}, + {"-1", "3", "2"}, + {"3", "-1", "2"}, + // OVERFLOW + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "0"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Add(x, y) + + if got.Neq(want) { + t.Errorf("Add(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestAddUint256(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "1"}, + {"1", "0", "1"}, + {"1", "1", "2"}, + {"1", "2", "3"}, + {"-1", "1", "0"}, + {"-1", "3", "2"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639934", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "1"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639934", "-1"}, + // OVERFLOW + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", "0"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.AddUint256(x, y) + + if got.Neq(want) { + t.Errorf("AddUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestAddDelta(t *testing.T) { + tests := []struct { + z, x, y, want string + }{ + {"0", "0", "0", "0"}, + {"0", "0", "1", "1"}, + {"0", "1", "0", "1"}, + {"0", "1", "1", "2"}, + {"1", "2", "3", "5"}, + {"5", "10", "-3", "7"}, + // underflow + {"1", "2", "-3", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + } + + for _, tc := range tests { + z, err := uint256.FromDecimal(tc.z) + if err != nil { + t.Error(err) + continue + } + + x, err := uint256.FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := uint256.FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + AddDelta(z, x, y) + + if z.Neq(want) { + t.Errorf("AddDelta(%s, %s, %s) = %v, want %v", tc.z, tc.x, tc.y, z.ToString(), want.ToString()) + } + } +} + +func TestAddDeltaOverflow(t *testing.T) { + tests := []struct { + z, x, y string + want bool + }{ + {"0", "0", "0", false}, + // underflow + {"1", "2", "-3", true}, + } + + for _, tc := range tests { + z, err := uint256.FromDecimal(tc.z) + if err != nil { + t.Error(err) + continue + } + + x, err := uint256.FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + result := AddDeltaOverflow(z, x, y) + if result != tc.want { + t.Errorf("AddDeltaOverflow(%s, %s, %s) = %v, want %v", tc.z, tc.x, tc.y, result, tc.want) + } + } +} + +func TestSub(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"1", "0", "1"}, + {"1", "1", "0"}, + {"-1", "1", "-2"}, + {"1", "-1", "2"}, + {"-1", "-1", "-0"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", "-0"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "0", "-115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + {x: "-115792089237316195423570985008687907853269984665640564039457584007913129639935", y: "1", want: "-0"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Sub(x, y) + + if got.Neq(want) { + t.Errorf("Sub(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestSubUint256(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "-1"}, + {"1", "0", "1"}, + {"1", "1", "0"}, + {"1", "2", "-1"}, + {"-1", "1", "-2"}, + {"-1", "3", "-4"}, + // underflow + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "1", "-0"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "2", "-1"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "3", "-2"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.SubUint256(x, y) + + if got.Neq(want) { + t.Errorf("SubUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestMul(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"5", "3", "15"}, + {"-5", "3", "-15"}, + {"5", "-3", "-15"}, + {"0", "3", "0"}, + {"3", "0", "0"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Mul(x, y) + + if got.Neq(want) { + t.Errorf("Mul(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestMulUint256(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"1", "0", "0"}, + {"1", "1", "1"}, + {"1", "2", "2"}, + {"-1", "1", "-1"}, + {"-1", "3", "-3"}, + {"3", "4", "12"}, + {"-3", "4", "-12"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "-115792089237316195423570985008687907853269984665640564039457584007913129639932"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "115792089237316195423570985008687907853269984665640564039457584007913129639932"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.MulUint256(x, y) + + if got.Neq(want) { + t.Errorf("MulUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestDiv(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"0", "-1", "-0"}, + {"10", "0", "0"}, + {"10", "1", "10"}, + {"10", "-1", "-10"}, + {"-10", "0", "-0"}, + {"-10", "1", "-10"}, + {"-10", "-1", "10"}, + {"10", "-3", "-3"}, + {"10", "3", "3"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Div(x, y) + + if got.Neq(want) { + t.Errorf("Div(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestDivUint256(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"1", "0", "0"}, + {"1", "1", "1"}, + {"1", "2", "0"}, + {"-1", "1", "-1"}, + {"-1", "3", "0"}, + {"4", "3", "1"}, + {"25", "5", "5"}, + {"25", "4", "6"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "-57896044618658097711785492504343953926634992332820282019728792003956564819967"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639934", "2", "57896044618658097711785492504343953926634992332820282019728792003956564819967"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := uint256.FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.DivUint256(x, y) + + if got.Neq(want) { + t.Errorf("DivUint256(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestQuo(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"0", "-1", "0"}, + {"10", "1", "10"}, + {"10", "-1", "-10"}, + {"-10", "1", "-10"}, + {"-10", "-1", "10"}, + {"10", "-3", "-3"}, + {"10", "3", "3"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Quo(x, y) + + if got.Neq(want) { + t.Errorf("Quo(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestRem(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"0", "-1", "0"}, + {"10", "1", "0"}, + {"10", "-1", "0"}, + {"-10", "1", "0"}, + {"-10", "-1", "0"}, + {"10", "3", "1"}, + {"10", "-3", "1"}, + {"-10", "3", "-1"}, + {"-10", "-3", "-1"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Rem(x, y) + + if got.Neq(want) { + t.Errorf("Rem(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestMod(t *testing.T) { + tests := []struct { + x, y, want string + }{ + {"0", "1", "0"}, + {"0", "-1", "0"}, + {"10", "0", "0"}, + {"10", "1", "0"}, + {"10", "-1", "0"}, + {"-10", "0", "0"}, + {"-10", "1", "0"}, + {"-10", "-1", "0"}, + {"10", "3", "1"}, + {"10", "-3", "1"}, + {"-10", "3", "2"}, + {"-10", "-3", "2"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := New() + got.Mod(x, y) + + if got.Neq(want) { + t.Errorf("Mod(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} 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..c0d0f65f78f --- /dev/null +++ b/examples/gno.land/p/demo/int256/bitwise.gno @@ -0,0 +1,94 @@ +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..ea51360ea65 --- /dev/null +++ b/examples/gno.land/p/demo/int256/bitwise_test.gno @@ -0,0 +1,201 @@ +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +import ( + "testing" +) + +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 new file mode 100644 index 00000000000..426dfd76485 --- /dev/null +++ b/examples/gno.land/p/demo/int256/cmp.gno @@ -0,0 +1,86 @@ +package int256 + +// Eq returns true if z == x +func (z *Int) Eq(x *Int) bool { + return (z.neg == x.neg) && z.abs.Eq(x.abs) +} + +// Neq returns true if z != x +func (z *Int) Neq(x *Int) bool { + return !z.Eq(x) +} + +// 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 +} + +// IsZero returns true if z == 0 +func (z *Int) IsZero() bool { + return z.abs.IsZero() +} + +// IsNeg returns true if z < 0 +func (z *Int) IsNeg() bool { + return z.neg +} + +// 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) + } + } +} + +// 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) + } + } +} + +// Clone creates a new Int identical to z +func (z *Int) Clone() *Int { + return &Int{z.abs.Clone(), z.neg} +} diff --git a/examples/gno.land/p/demo/int256/cmp_test.gno b/examples/gno.land/p/demo/int256/cmp_test.gno new file mode 100644 index 00000000000..155cfa94b92 --- /dev/null +++ b/examples/gno.land/p/demo/int256/cmp_test.gno @@ -0,0 +1,263 @@ +package int256 + +import ( + "testing" +) + +func TestEq(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"0", "0", true}, + {"0", "1", false}, + {"1", "0", false}, + {"-1", "0", false}, + {"0", "-1", false}, + {"1", "1", true}, + {"-1", "-1", true}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Eq(y) + if got != tc.want { + t.Errorf("Eq(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestNeq(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"0", "0", false}, + {"0", "1", true}, + {"1", "0", true}, + {"-1", "0", true}, + {"0", "-1", true}, + {"1", "1", false}, + {"-1", "-1", false}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Neq(y) + if got != tc.want { + t.Errorf("Neq(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestCmp(t *testing.T) { + tests := []struct { + x, y string + want int + }{ + {"0", "0", 0}, + {"0", "1", -1}, + {"1", "0", 1}, + {"-1", "0", -1}, + {"0", "-1", 1}, + {"1", "1", 0}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", 1}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Cmp(y) + if got != tc.want { + t.Errorf("Cmp(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestIsZero(t *testing.T) { + tests := []struct { + x string + want bool + }{ + {"0", true}, + {"-0", true}, + {"1", false}, + {"-1", false}, + {"10", false}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := x.IsZero() + if got != tc.want { + t.Errorf("IsZero(%s) = %v, want %v", tc.x, got, tc.want) + } + } +} + +func TestIsNeg(t *testing.T) { + tests := []struct { + x string + want bool + }{ + {"0", false}, + {"-0", true}, // TODO: should this be false? + {"1", false}, + {"-1", true}, + {"10", false}, + {"-10", true}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + got := x.IsNeg() + if got != tc.want { + t.Errorf("IsNeg(%s) = %v, want %v", tc.x, got, tc.want) + } + } +} + +func TestLt(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"0", "0", false}, + {"0", "1", true}, + {"1", "0", false}, + {"-1", "0", true}, + {"0", "-1", false}, + {"1", "1", false}, + {"-1", "-1", false}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", false}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Lt(y) + if got != tc.want { + t.Errorf("Lt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestGt(t *testing.T) { + tests := []struct { + x, y string + want bool + }{ + {"0", "0", false}, + {"0", "1", false}, + {"1", "0", true}, + {"-1", "0", false}, + {"0", "-1", true}, + {"1", "1", false}, + {"-1", "-1", false}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "-115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := FromDecimal(tc.y) + if err != nil { + t.Error(err) + continue + } + + got := x.Gt(y) + if got != tc.want { + t.Errorf("Gt(%s, %s) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestClone(t *testing.T) { + tests := []struct { + x string + }{ + {"0"}, + {"-0"}, + {"1"}, + {"-1"}, + {"10"}, + {"-10"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + {"-115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + y := x.Clone() + + if x.Cmp(y) != 0 { + t.Errorf("Clone(%s) = %v, want %v", tc.x, y, x) + } + } +} diff --git a/examples/gno.land/p/demo/int256/conversion.gno b/examples/gno.land/p/demo/int256/conversion.gno new file mode 100644 index 00000000000..c6955f37421 --- /dev/null +++ b/examples/gno.land/p/demo/int256/conversion.gno @@ -0,0 +1,88 @@ +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +// SetInt64 sets z to x and returns z. +func (z *Int) SetInt64(x int64) *Int { + z.initiateAbs() + + neg := false + if x < 0 { + neg = true + x = -x + } + if z.abs == nil { + panic("abs is nil") + } + z.abs = z.abs.SetUint64(uint64(x)) + z.neg = neg + return z +} + +// SetUint64 sets z to x and returns z. +func (z *Int) SetUint64(x uint64) *Int { + z.initiateAbs() + + if z.abs == nil { + panic("abs is nil") + } + z.abs = z.abs.SetUint64(x) + z.neg = false + return z +} + +// Uint64 returns the lower 64-bits of z +func (z *Int) Uint64() uint64 { + return z.abs.Uint64() +} + +// Int64 returns the lower 64-bits of z +func (z *Int) Int64() int64 { + _abs := z.abs.Clone() + + if z.neg { + return -int64(_abs.Uint64()) + } + return int64(_abs.Uint64()) +} + +// Neg sets z to -x and returns z.) +func (z *Int) Neg(x *Int) *Int { + z.abs.Set(x.abs) + if z.abs.IsZero() { + z.neg = false + } else { + z.neg = !x.neg + } + return z +} + +// Set sets z to x and returns z. +func (z *Int) Set(x *Int) *Int { + z.abs.Set(x.abs) + z.neg = x.neg + return z +} + +// SetFromUint256 converts a uint256.Uint to Int and sets the value to z. +func (z *Int) SetUint256(x *uint256.Uint) *Int { + z.abs.Set(x) + z.neg = false + return z +} + +// OBS, differs from original mempooler int256 +// ToString returns the decimal representation of z. +func (z *Int) ToString() string { + if z == nil { + panic("int256: nil pointer to ToString()") + } + + t := z.abs.Dec() + if z.neg { + return "-" + t + } + return t +} diff --git a/examples/gno.land/p/demo/int256/conversion_test.gno b/examples/gno.land/p/demo/int256/conversion_test.gno new file mode 100644 index 00000000000..da54c226669 --- /dev/null +++ b/examples/gno.land/p/demo/int256/conversion_test.gno @@ -0,0 +1,171 @@ +package int256 + +import ( + "testing" + + "gno.land/p/demo/uint256" +) + +func TestSetInt64(t *testing.T) { + tests := []struct { + x int64 + want string + }{ + {0, "0"}, + {1, "1"}, + {-1, "-1"}, + {9223372036854775807, "9223372036854775807"}, + {-9223372036854775808, "-9223372036854775808"}, + } + + for _, tc := range tests { + var z Int + z.SetInt64(tc.x) + + got := z.ToString() + if got != tc.want { + t.Errorf("SetInt64(%d) = %s, want %s", tc.x, got, tc.want) + } + } +} + +func TestSetUint64(t *testing.T) { + tests := []struct { + x uint64 + want string + }{ + {0, "0"}, + {1, "1"}, + } + + for _, tc := range tests { + var z Int + z.SetUint64(tc.x) + + got := z.ToString() + if got != tc.want { + t.Errorf("SetUint64(%d) = %s, want %s", tc.x, got, tc.want) + } + } +} + +func TestUint64(t *testing.T) { + tests := []struct { + x string + want uint64 + }{ + {"0", 0}, + {"1", 1}, + {"9223372036854775807", 9223372036854775807}, + {"9223372036854775808", 9223372036854775808}, + {"18446744073709551615", 18446744073709551615}, + {"18446744073709551616", 0}, + {"18446744073709551617", 1}, + {"-1", 1}, + {"-18446744073709551615", 18446744073709551615}, + {"-18446744073709551616", 0}, + {"-18446744073709551617", 1}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + + got := z.Uint64() + if got != tc.want { + t.Errorf("Uint64(%s) = %d, want %d", tc.x, got, tc.want) + } + } +} + +func TestInt64(t *testing.T) { + tests := []struct { + x string + want int64 + }{ + {"0", 0}, + {"1", 1}, + {"9223372036854775807", 9223372036854775807}, + {"18446744073709551616", 0}, + {"18446744073709551617", 1}, + {"-1", -1}, + {"-9223372036854775808", -9223372036854775808}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + + got := z.Int64() + if got != tc.want { + t.Errorf("Uint64(%s) = %d, want %d", tc.x, got, tc.want) + } + } +} + +func TestNeg(t *testing.T) { + tests := []struct { + x string + want string + }{ + {"0", "0"}, + {"1", "-1"}, + {"-1", "1"}, + {"9223372036854775807", "-9223372036854775807"}, + {"-18446744073709551615", "18446744073709551615"}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + z.Neg(z) + + got := z.ToString() + if got != tc.want { + t.Errorf("Neg(%s) = %s, want %s", tc.x, got, tc.want) + } + } +} + +func TestSet(t *testing.T) { + tests := []struct { + x string + want string + }{ + {"0", "0"}, + {"1", "1"}, + {"-1", "-1"}, + {"9223372036854775807", "9223372036854775807"}, + {"-18446744073709551615", "-18446744073709551615"}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + z.Set(z) + + got := z.ToString() + if got != tc.want { + t.Errorf("Set(%s) = %s, want %s", tc.x, got, tc.want) + } + } +} + +func TestSetUint256(t *testing.T) { + tests := []struct { + x string + want string + }{ + {"0", "0"}, + {"1", "1"}, + {"9223372036854775807", "9223372036854775807"}, + {"18446744073709551615", "18446744073709551615"}, + } + + for _, tc := range tests { + got := New() + + z := uint256.MustFromDecimal(tc.x) + got.SetUint256(z) + + if got.ToString() != tc.want { + t.Errorf("SetUint256(%s) = %s, want %s", tc.x, got.ToString(), tc.want) + } + } +} diff --git a/examples/gno.land/p/demo/int256/gno.mod b/examples/gno.land/p/demo/int256/gno.mod new file mode 100644 index 00000000000..ef906c83c93 --- /dev/null +++ b/examples/gno.land/p/demo/int256/gno.mod @@ -0,0 +1,3 @@ +module gno.land/p/demo/int256 + +require gno.land/p/demo/uint256 v0.0.0-latest diff --git a/examples/gno.land/p/demo/int256/int256.gno b/examples/gno.land/p/demo/int256/int256.gno new file mode 100644 index 00000000000..caccd17d531 --- /dev/null +++ b/examples/gno.land/p/demo/int256/int256.gno @@ -0,0 +1,126 @@ +// This package provides a 256-bit signed integer type, Int, and associated functions. +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +var one = uint256.NewUint(1) + +type Int struct { + abs *uint256.Uint + neg bool +} + +// Zero returns a new Int set to 0. +func Zero() *Int { + return NewInt(0) +} + +// One returns a new Int set to 1. +func One() *Int { + return NewInt(1) +} + +// Sign returns: +// +// -1 if x < 0 +// 0 if x == 0 +// +1 if x > 0 +func (z *Int) Sign() int { + z.initiateAbs() + + if z.abs.IsZero() { + return 0 + } + if z.neg { + return -1 + } + return 1 +} + +// New returns a new Int set to 0. +func New() *Int { + return &Int{ + abs: new(uint256.Uint), + } +} + +// NewInt allocates and returns a new Int set to x. +func NewInt(x int64) *Int { + return New().SetInt64(x) +} + +// FromDecimal returns a new Int from a decimal string. +// Returns a new Int and an error if the string is not a valid decimal. +func FromDecimal(s string) (*Int, error) { + return new(Int).SetString(s) +} + +// MustFromDecimal returns a new Int from a decimal string. +// Panics if the string is not a valid decimal. +func MustFromDecimal(s string) *Int { + z, err := FromDecimal(s) + if err != nil { + panic(err) + } + return z +} + +// SetString sets s to the value of z and returns z and a boolean indicating success. +func (z *Int) SetString(s string) (*Int, error) { + neg := false + // Remove max one leading + + if len(s) > 0 && s[0] == '+' { + neg = false + s = s[1:] + } + + if len(s) > 0 && s[0] == '-' { + neg = true + s = s[1:] + } + var ( + abs *uint256.Uint + err error + ) + abs, err = uint256.FromDecimal(s) + if err != nil { + return nil, err + } + + return &Int{ + abs, + neg, + }, nil +} + +// FromUint256 is a convenience-constructor from uint256.Uint. +// Returns a new Int and whether overflow occurred. +// OBS: If u is `nil`, this method returns `nil, false` +func FromUint256(x *uint256.Uint) *Int { + if x == nil { + return nil + } + z := Zero() + + z.SetUint256(x) + return z +} + +// OBS, differs from original mempooler int256 +// NilToZero sets z to 0 and return it if it's nil, otherwise it returns z +func (z *Int) NilToZero() *Int { + if z == nil { + return NewInt(0) + } + return z +} + +// initiateAbs sets default value for `z` or `z.abs` value if is nil +// OBS: differs from mempooler int256. It checks not only `z.abs` but also `z` +func (z *Int) initiateAbs() { + if z == nil || z.abs == nil { + z.abs = new(uint256.Uint) + } +} diff --git a/examples/gno.land/p/demo/int256/int256_test.gno b/examples/gno.land/p/demo/int256/int256_test.gno new file mode 100644 index 00000000000..974cc6ed87f --- /dev/null +++ b/examples/gno.land/p/demo/int256/int256_test.gno @@ -0,0 +1,25 @@ +// ported from github.com/mempooler/int256 +package int256 + +import ( + "testing" +) + +func TestSign(t *testing.T) { + tests := []struct { + x string + want int + }{ + {"0", 0}, + {"1", 1}, + {"-1", -1}, + } + + for _, tc := range tests { + z := MustFromDecimal(tc.x) + got := z.Sign() + if got != tc.want { + t.Errorf("Sign(%s) = %d, want %d", tc.x, got, tc.want) + } + } +}