-
Notifications
You must be signed in to change notification settings - Fork 379
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This pr implements `int256` package to handle huge numbers in gno. To support this, it uses `uint256` package in its own struct > there is pr to bring holiman's uint256 into gno-vm #1778 origin(go) implementation: https://github.com/mempooler/int256 gnoswap(gno) implementation: [https://github.com/gnoswap-labs/gnoswap/package/big/int256](https://github.com/gnoswap-labs/gnoswap/tree/51439a5b097e6c523cb8a77b1a792b7f7aacb863/packages/big/int256 ) ### gno test metrics ```bash gno test -v=true -print-runtime-metrics=true ./examples/gno.land/p/demo/int256 === RUN TestAbs --- PASS: TestAbs (0.00s) --- runtime: cycle=30.5k imports=3 allocs=797.6k(0.16%) === RUN TestAbsGt --- PASS: TestAbsGt (0.00s) --- runtime: cycle=108.7k imports=3 allocs=2.5M(0.50%) === RUN TestAdd --- PASS: TestAdd (0.00s) --- runtime: cycle=155.2k imports=3 allocs=4.0M(0.79%) === RUN TestSub --- PASS: TestSub (0.00s) --- runtime: cycle=259.1k imports=3 allocs=6.3M(1.26%) === RUN TestMul --- PASS: TestMul (0.00s) --- runtime: cycle=278.4k imports=3 allocs=7.0M(1.39%) === RUN TestDiv --- PASS: TestDiv (0.00s) --- runtime: cycle=307.6k imports=3 allocs=8.1M(1.62%) === RUN TestRem --- PASS: TestRem (0.00s) --- runtime: cycle=342.8k imports=3 allocs=9.5M(1.90%) === RUN TestEq --- PASS: TestEq (0.00s) --- runtime: cycle=422.1k imports=3 allocs=11.3M(2.25%) === RUN TestCmp --- PASS: TestCmp (0.00s) --- runtime: cycle=466.7k imports=3 allocs=12.4M(2.47%) === RUN TestIsZero --- PASS: TestIsZero (0.00s) --- runtime: cycle=471.3k imports=3 allocs=12.6M(2.51%) === RUN TestIsNeg --- PASS: TestIsNeg (0.00s) --- runtime: cycle=476.6k imports=3 allocs=12.8M(2.56%) === RUN TestLt --- PASS: TestLt (0.00s) --- runtime: cycle=522.7k imports=3 allocs=14.0M(2.79%) === RUN TestClone --- PASS: TestClone (0.00s) --- runtime: cycle=565.1k imports=3 allocs=14.9M(2.98%) === RUN TestSetInt64 --- PASS: TestSetInt64 (0.00s) --- runtime: cycle=567.2k imports=3 allocs=15.0M(3.01%) === RUN TestSetUint64 --- PASS: TestSetUint64 (0.00s) --- runtime: cycle=568.3k imports=3 allocs=15.1M(3.02%) === RUN TestUint64 --- PASS: TestUint64 (0.00s) --- runtime: cycle=605.7k imports=3 allocs=16.0M(3.21%) === RUN TestInt64 --- PASS: TestInt64 (0.00s) --- runtime: cycle=624.5k imports=3 allocs=16.5M(3.31%) === RUN TestNeg --- PASS: TestNeg (0.00s) --- runtime: cycle=636.2k imports=3 allocs=16.9M(3.38%) === RUN TestSign --- PASS: TestSign (0.00s) --- runtime: cycle=639.4k imports=3 allocs=17.1M(3.41%) ok ./examples/gno.land/p/demo/int256 0.59s ``` <!-- please provide a detailed description of the changes made in this pull request. --> <details><summary>Contributors' checklist...</summary> - [TODO] Added new tests, or not needed, or not feasible - [ ] Provided an example (e.g. screenshot) to aid review or the PR is self-explanatory - [x] Updated the official documentation or not needed - [x] No breaking changes were made, or a `BREAKING CHANGE: xxx` message was included in the description - [x] Added references to related issues and PRs - [ ] Provided any useful hints for running manual tests - [TODO] Added new benchmarks to [generated graphs](https://gnoland.github.io/benchmarks), if any. More info [here](https://github.com/gnolang/gno/blob/master/.benchmarks/README.md). </details>
- Loading branch information
Showing
15 changed files
with
1,978 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
Oops, something went wrong.