From 305adf5a115ce57a95731d8bcd1295b06f7fac29 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Fri, 15 Mar 2024 17:37:56 +0900 Subject: [PATCH 01/23] WIP: uint256 --- .../gno.land/p/demo/uint256/bits_errors.gno | 11 + .../gno.land/p/demo/uint256/bits_table.gno | 79 ++ examples/gno.land/p/demo/uint256/consts.gno | 5 + examples/gno.land/p/demo/uint256/gno.mod | 1 + .../gno.land/p/demo/uint256/int256/gno.mod | 3 + .../gno.land/p/demo/uint256/int256/int256.gno | 557 ++++++++ .../p/demo/uint256/int256/int256_test.gno | 1 + examples/gno.land/p/demo/uint256/mod.gno | 629 ++++++++ examples/gno.land/p/demo/uint256/uint256.gno | 1264 +++++++++++++++++ .../uint256/uint256_overflow_calculation.gno | 99 ++ .../gno.land/p/demo/uint256/uint256_test.gno | 18 + 11 files changed, 2667 insertions(+) create mode 100644 examples/gno.land/p/demo/uint256/bits_errors.gno create mode 100644 examples/gno.land/p/demo/uint256/bits_table.gno create mode 100644 examples/gno.land/p/demo/uint256/consts.gno create mode 100644 examples/gno.land/p/demo/uint256/gno.mod create mode 100644 examples/gno.land/p/demo/uint256/int256/gno.mod create mode 100644 examples/gno.land/p/demo/uint256/int256/int256.gno create mode 100644 examples/gno.land/p/demo/uint256/int256/int256_test.gno create mode 100644 examples/gno.land/p/demo/uint256/mod.gno create mode 100644 examples/gno.land/p/demo/uint256/uint256.gno create mode 100644 examples/gno.land/p/demo/uint256/uint256_overflow_calculation.gno create mode 100644 examples/gno.land/p/demo/uint256/uint256_test.gno diff --git a/examples/gno.land/p/demo/uint256/bits_errors.gno b/examples/gno.land/p/demo/uint256/bits_errors.gno new file mode 100644 index 00000000000..5de53099802 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/bits_errors.gno @@ -0,0 +1,11 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +package uint256 + +import "errors" + +var ( + overflowError = errors.New("u256: integer overflow") + divideError = errors.New("u256: integer divide by zero") +) \ No newline at end of file diff --git a/examples/gno.land/p/demo/uint256/bits_table.gno b/examples/gno.land/p/demo/uint256/bits_table.gno new file mode 100644 index 00000000000..7cb2081fd19 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/bits_table.gno @@ -0,0 +1,79 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by go run make_tables.go. DO NOT EDIT. + +package uint256 + +const ntz8tab = "" + + "\x08\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x06\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x07\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x06\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + +const pop8tab = "" + + "\x00\x01\x01\x02\x01\x02\x02\x03\x01\x02\x02\x03\x02\x03\x03\x04" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x04\x05\x05\x06\x05\x06\x06\x07\x05\x06\x06\x07\x06\x07\x07\x08" + +const rev8tab = "" + + "\x00\x80\x40\xc0\x20\xa0\x60\xe0\x10\x90\x50\xd0\x30\xb0\x70\xf0" + + "\x08\x88\x48\xc8\x28\xa8\x68\xe8\x18\x98\x58\xd8\x38\xb8\x78\xf8" + + "\x04\x84\x44\xc4\x24\xa4\x64\xe4\x14\x94\x54\xd4\x34\xb4\x74\xf4" + + "\x0c\x8c\x4c\xcc\x2c\xac\x6c\xec\x1c\x9c\x5c\xdc\x3c\xbc\x7c\xfc" + + "\x02\x82\x42\xc2\x22\xa2\x62\xe2\x12\x92\x52\xd2\x32\xb2\x72\xf2" + + "\x0a\x8a\x4a\xca\x2a\xaa\x6a\xea\x1a\x9a\x5a\xda\x3a\xba\x7a\xfa" + + "\x06\x86\x46\xc6\x26\xa6\x66\xe6\x16\x96\x56\xd6\x36\xb6\x76\xf6" + + "\x0e\x8e\x4e\xce\x2e\xae\x6e\xee\x1e\x9e\x5e\xde\x3e\xbe\x7e\xfe" + + "\x01\x81\x41\xc1\x21\xa1\x61\xe1\x11\x91\x51\xd1\x31\xb1\x71\xf1" + + "\x09\x89\x49\xc9\x29\xa9\x69\xe9\x19\x99\x59\xd9\x39\xb9\x79\xf9" + + "\x05\x85\x45\xc5\x25\xa5\x65\xe5\x15\x95\x55\xd5\x35\xb5\x75\xf5" + + "\x0d\x8d\x4d\xcd\x2d\xad\x6d\xed\x1d\x9d\x5d\xdd\x3d\xbd\x7d\xfd" + + "\x03\x83\x43\xc3\x23\xa3\x63\xe3\x13\x93\x53\xd3\x33\xb3\x73\xf3" + + "\x0b\x8b\x4b\xcb\x2b\xab\x6b\xeb\x1b\x9b\x5b\xdb\x3b\xbb\x7b\xfb" + + "\x07\x87\x47\xc7\x27\xa7\x67\xe7\x17\x97\x57\xd7\x37\xb7\x77\xf7" + + "\x0f\x8f\x4f\xcf\x2f\xaf\x6f\xef\x1f\x9f\x5f\xdf\x3f\xbf\x7f\xff" + +const len8tab = "" + + "\x00\x01\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04\x04\x04\x04\x04" + + "\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05" + + "\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06" + + "\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" \ No newline at end of file diff --git a/examples/gno.land/p/demo/uint256/consts.gno b/examples/gno.land/p/demo/uint256/consts.gno new file mode 100644 index 00000000000..2de43d58262 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/consts.gno @@ -0,0 +1,5 @@ +package uint256 + +const ( + MAX_UINT256 = "115792089237316195423570985008687907853269984665640564039457584007913129639935" +) diff --git a/examples/gno.land/p/demo/uint256/gno.mod b/examples/gno.land/p/demo/uint256/gno.mod new file mode 100644 index 00000000000..71e5050c831 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/gno.mod @@ -0,0 +1 @@ +module gno.land/p/demo/uint256 diff --git a/examples/gno.land/p/demo/uint256/int256/gno.mod b/examples/gno.land/p/demo/uint256/int256/gno.mod new file mode 100644 index 00000000000..ef906c83c93 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/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/uint256/int256/int256.gno b/examples/gno.land/p/demo/uint256/int256/int256.gno new file mode 100644 index 00000000000..30412935184 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/int256/int256.gno @@ -0,0 +1,557 @@ +// github.com/mempooler/int256 + +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +var one = uint256.NewUint(1) + +type Int struct { + abs *uint256.Uint + neg bool +} + +func Zero() *Int { + return NewInt(0) +} + +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 { + if len(z.abs) == 0 { + return 0 + } + if z.neg { + return -1 + } + return 1 +} + +func New() *Int { + return &Int{ + abs: new(uint256.Uint), + } +} + +// SetInt64 sets z to x and returns z. +func (z *Int) SetInt64(x int64) *Int { + 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 { + if z.abs == nil { + panic("abs is nil") + } + z.abs = z.abs.SetUint64(x) + z.neg = false + return z +} + +// NewInt allocates and returns a new Int set to x. +func NewInt(x int64) *Int { + return New().SetInt64(x) +} + +func FromDecimal(s string) (*Int, error) { + return new(Int).SetString(s) +} + +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) { + origin := s + 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 +} + +// // setFromScanner implements SetString given an io.ByteScanner. +// // For documentation see comments of SetString. +// func (z *Int) setFromScanner(r io.ByteScanner, base int) (*Int, bool) { +// if _, _, err := z.scan(r, base); err != nil { +// return nil, false +// } +// // entire content must have been consumed +// if _, err := r.ReadByte(); err != io.EOF { +// return nil, false +// } +// return z, true // err == io.EOF => scan consumed all content of r +// } + +func (z *Int) Add(x, y *Int) *Int { + 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 +} + +// Sub sets z to the difference x-y and returns z. +func (z *Int) Sub(x, y *Int) *Int { + 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 +} + +// Mul sets z to the product x*y and returns z. +func (z *Int) Mul(x, y *Int) *Int { + z.abs = z.abs.Mul(x.abs, y.abs) + z.neg = x.neg != y.neg // 0 has no sign + return z +} + +// Rsh sets z = x >> n and returns z. +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(0)) + t = t.Rsh(t, n) + + _tmp := t.Add(t, NewInt(1)) + z.abs = _tmp.Abs() + z.neg = true + + 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. +// Quo implements truncated division (like Go); see QuoRem for more details. +func (z *Int) Quo(x, y *Int) *Int { + z.abs = z.abs.Div(x.abs, y.abs) + z.neg = len(z.abs) > 0 && 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. +// Rem implements truncated modulus (like Go); see QuoRem for more details. +func (z *Int) Rem(x, y *Int) *Int { + z.abs.Mod(x.abs, y.abs) + z.neg = len(z.abs) > 0 && x.neg // 0 has no sign + return z +} + +// 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 +} + +func (z *Int) Div(x, y *Int) *Int { + z.abs.Div(x.abs, y.abs) + if x.neg == y.neg { + z.neg = false + } else { + 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 +} + +// 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 +} + +// 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 + + // // TODO: implement + // big := new(big.Int).Or(x.ToBig(), y.ToBig()) + // z = MustFromBig(big) + return z +} + +// 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 := New() + // z := &Int{} + z.SetUint256(x) + return z +} + +// 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) +} + +// 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 +} + +// Clone creates a new Int identical to z +func (z *Int) Clone() *Int { + return &Int{z.abs.Clone(), z.neg} +} + +// 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 +} + +// Eq returns true if z == x +func (z *Int) Eq(x *Int) bool { + return (z.neg == x.neg) && z.abs.Eq(x.abs) +} + +// 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) + } + } +} + +// 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 +} + +// 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 +} + +// 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 +} + +// 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 +} + +// 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()) +} + +// 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 +} + +// NIL TO ZERO +func (z *Int) NilToZero() *Int { + if z == nil { + return NewInt(0) + } + return z +} + +func (z *Int) ToString() string { + if z == nil { + panic("int256: nil pointer") + } + + t := z.abs.Dec() + if z.neg { + return "-" + t + } + return t +} diff --git a/examples/gno.land/p/demo/uint256/int256/int256_test.gno b/examples/gno.land/p/demo/uint256/int256/int256_test.gno new file mode 100644 index 00000000000..e5b8059855c --- /dev/null +++ b/examples/gno.land/p/demo/uint256/int256/int256_test.gno @@ -0,0 +1 @@ +package int256 diff --git a/examples/gno.land/p/demo/uint256/mod.gno b/examples/gno.land/p/demo/uint256/mod.gno new file mode 100644 index 00000000000..16f312ea3b2 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/mod.gno @@ -0,0 +1,629 @@ +// uint256: Fixed size 256-bit math library +// Copyright 2021 uint256 Authors +// SPDX-License-Identifier: BSD-3-Clause + +package uint256 + +import ( + "math/bits" +) + +// Some utility functions + +func leadingZeros(x *Uint) (z int) { + var t int + z = bits.LeadingZeros64(x.arr[3]) + t = bits.LeadingZeros64(x.arr[2]) + if z == 64 { + z = t + 64 + } + t = bits.LeadingZeros64(x.arr[1]) + if z == 128 { + z = t + 128 + } + t = bits.LeadingZeros64(x.arr[0]) + if z == 192 { + z = t + 192 + } + return z +} + +// Reciprocal computes a 320-bit value representing 1/m +// +// Notes: +// - specialized for m.arr[3] != 0, hence limited to 2^192 <= m < 2^256 +// - returns zero if m.arr[3] == 0 +// - starts with a 32-bit division, refines with newton-raphson iterations +func Reciprocal(m *Uint) (mu [5]uint64) { + + if m.arr[3] == 0 { + return mu + } + + s := bits.LeadingZeros64(m.arr[3]) // Replace with leadingZeros(m) for general case + p := 255 - s // floor(log_2(m)), m>0 + + // 0 or a power of 2? + + // Check if at least one bit is set in m.arr[2], m.arr[1] or m.arr[0], + // or at least two bits in m.arr[3] + + if m.arr[0]|m.arr[1]|m.arr[2]|(m.arr[3]&(m.arr[3]-1)) == 0 { + + mu[4] = ^uint64(0) >> uint(p&63) + mu[3] = ^uint64(0) + mu[2] = ^uint64(0) + mu[1] = ^uint64(0) + mu[0] = ^uint64(0) + + return mu + } + + // Maximise division precision by left-aligning divisor + + var ( + y Uint // left-aligned copy of m + r0 uint32 // estimate of 2^31/y + ) + + y.Lsh(m, uint(s)) // 1/2 < y < 1 + + // Extract most significant 32 bits + + yh := uint32(y.arr[3] >> 32) + + if yh == 0x80000000 { // Avoid overflow in division + r0 = 0xffffffff + } else { + r0, _ = bits.Div32(0x80000000, 0, yh) + } + + // First iteration: 32 -> 64 + + t1 := uint64(r0) // 2^31/y + t1 *= t1 // 2^62/y^2 + t1, _ = bits.Mul64(t1, y.arr[3]) // 2^62/y^2 * 2^64/y / 2^64 = 2^62/y + + r1 := uint64(r0) << 32 // 2^63/y + r1 -= t1 // 2^63/y - 2^62/y = 2^62/y + r1 *= 2 // 2^63/y + + if (r1 | (y.arr[3] << 1)) == 0 { + r1 = ^uint64(0) + } + + // Second iteration: 64 -> 128 + + // square: 2^126/y^2 + a2h, a2l := bits.Mul64(r1, r1) + + // multiply by y: e2h:e2l:b2h = 2^126/y^2 * 2^128/y / 2^128 = 2^126/y + b2h, _ := bits.Mul64(a2l, y.arr[2]) + c2h, c2l := bits.Mul64(a2l, y.arr[3]) + d2h, d2l := bits.Mul64(a2h, y.arr[2]) + e2h, e2l := bits.Mul64(a2h, y.arr[3]) + + b2h, c := bits.Add64(b2h, c2l, 0) + e2l, c = bits.Add64(e2l, c2h, c) + e2h, _ = bits.Add64(e2h, 0, c) + + _, c = bits.Add64(b2h, d2l, 0) + e2l, c = bits.Add64(e2l, d2h, c) + e2h, _ = bits.Add64(e2h, 0, c) + + // subtract: t2h:t2l = 2^127/y - 2^126/y = 2^126/y + t2l, b := bits.Sub64(0, e2l, 0) + t2h, _ := bits.Sub64(r1, e2h, b) + + // double: r2h:r2l = 2^127/y + r2l, c := bits.Add64(t2l, t2l, 0) + r2h, _ := bits.Add64(t2h, t2h, c) + + if (r2h | r2l | (y.arr[3] << 1)) == 0 { + r2h = ^uint64(0) + r2l = ^uint64(0) + } + + // Third iteration: 128 -> 192 + + // square r2 (keep 256 bits): 2^190/y^2 + a3h, a3l := bits.Mul64(r2l, r2l) + b3h, b3l := bits.Mul64(r2l, r2h) + c3h, c3l := bits.Mul64(r2h, r2h) + + a3h, c = bits.Add64(a3h, b3l, 0) + c3l, c = bits.Add64(c3l, b3h, c) + c3h, _ = bits.Add64(c3h, 0, c) + + a3h, c = bits.Add64(a3h, b3l, 0) + c3l, c = bits.Add64(c3l, b3h, c) + c3h, _ = bits.Add64(c3h, 0, c) + + // multiply by y: q = 2^190/y^2 * 2^192/y / 2^192 = 2^190/y + + x0 := a3l + x1 := a3h + x2 := c3l + x3 := c3h + + var q0, q1, q2, q3, q4, t0 uint64 + + q0, _ = bits.Mul64(x2, y.arr[0]) + q1, t0 = bits.Mul64(x3, y.arr[0]) + q0, c = bits.Add64(q0, t0, 0) + q1, _ = bits.Add64(q1, 0, c) + + t1, _ = bits.Mul64(x1, y.arr[1]) + q0, c = bits.Add64(q0, t1, 0) + q2, t0 = bits.Mul64(x3, y.arr[1]) + q1, c = bits.Add64(q1, t0, c) + q2, _ = bits.Add64(q2, 0, c) + + t1, t0 = bits.Mul64(x2, y.arr[1]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + q2, _ = bits.Add64(q2, 0, c) + + t1, t0 = bits.Mul64(x1, y.arr[2]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + q3, t0 = bits.Mul64(x3, y.arr[2]) + q2, c = bits.Add64(q2, t0, c) + q3, _ = bits.Add64(q3, 0, c) + + t1, _ = bits.Mul64(x0, y.arr[2]) + q0, c = bits.Add64(q0, t1, 0) + t1, t0 = bits.Mul64(x2, y.arr[2]) + q1, c = bits.Add64(q1, t0, c) + q2, c = bits.Add64(q2, t1, c) + q3, _ = bits.Add64(q3, 0, c) + + t1, t0 = bits.Mul64(x1, y.arr[3]) + q1, c = bits.Add64(q1, t0, 0) + q2, c = bits.Add64(q2, t1, c) + q4, t0 = bits.Mul64(x3, y.arr[3]) + q3, c = bits.Add64(q3, t0, c) + q4, _ = bits.Add64(q4, 0, c) + + t1, t0 = bits.Mul64(x0, y.arr[3]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + t1, t0 = bits.Mul64(x2, y.arr[3]) + q2, c = bits.Add64(q2, t0, c) + q3, c = bits.Add64(q3, t1, c) + q4, _ = bits.Add64(q4, 0, c) + + // subtract: t3 = 2^191/y - 2^190/y = 2^190/y + _, b = bits.Sub64(0, q0, 0) + _, b = bits.Sub64(0, q1, b) + t3l, b := bits.Sub64(0, q2, b) + t3m, b := bits.Sub64(r2l, q3, b) + t3h, _ := bits.Sub64(r2h, q4, b) + + // double: r3 = 2^191/y + r3l, c := bits.Add64(t3l, t3l, 0) + r3m, c := bits.Add64(t3m, t3m, c) + r3h, _ := bits.Add64(t3h, t3h, c) + + // Fourth iteration: 192 -> 320 + + // square r3 + + a4h, a4l := bits.Mul64(r3l, r3l) + b4h, b4l := bits.Mul64(r3l, r3m) + c4h, c4l := bits.Mul64(r3l, r3h) + d4h, d4l := bits.Mul64(r3m, r3m) + e4h, e4l := bits.Mul64(r3m, r3h) + f4h, f4l := bits.Mul64(r3h, r3h) + + b4h, c = bits.Add64(b4h, c4l, 0) + e4l, c = bits.Add64(e4l, c4h, c) + e4h, _ = bits.Add64(e4h, 0, c) + + a4h, c = bits.Add64(a4h, b4l, 0) + d4l, c = bits.Add64(d4l, b4h, c) + d4h, c = bits.Add64(d4h, e4l, c) + f4l, c = bits.Add64(f4l, e4h, c) + f4h, _ = bits.Add64(f4h, 0, c) + + a4h, c = bits.Add64(a4h, b4l, 0) + d4l, c = bits.Add64(d4l, b4h, c) + d4h, c = bits.Add64(d4h, e4l, c) + f4l, c = bits.Add64(f4l, e4h, c) + f4h, _ = bits.Add64(f4h, 0, c) + + // multiply by y + + x1, x0 = bits.Mul64(d4h, y.arr[0]) + x3, x2 = bits.Mul64(f4h, y.arr[0]) + t1, t0 = bits.Mul64(f4l, y.arr[0]) + x1, c = bits.Add64(x1, t0, 0) + x2, c = bits.Add64(x2, t1, c) + x3, _ = bits.Add64(x3, 0, c) + + t1, t0 = bits.Mul64(d4h, y.arr[1]) + x1, c = bits.Add64(x1, t0, 0) + x2, c = bits.Add64(x2, t1, c) + x4, t0 := bits.Mul64(f4h, y.arr[1]) + x3, c = bits.Add64(x3, t0, c) + x4, _ = bits.Add64(x4, 0, c) + t1, t0 = bits.Mul64(d4l, y.arr[1]) + x0, c = bits.Add64(x0, t0, 0) + x1, c = bits.Add64(x1, t1, c) + t1, t0 = bits.Mul64(f4l, y.arr[1]) + x2, c = bits.Add64(x2, t0, c) + x3, c = bits.Add64(x3, t1, c) + x4, _ = bits.Add64(x4, 0, c) + + t1, t0 = bits.Mul64(a4h, y.arr[2]) + x0, c = bits.Add64(x0, t0, 0) + x1, c = bits.Add64(x1, t1, c) + t1, t0 = bits.Mul64(d4h, y.arr[2]) + x2, c = bits.Add64(x2, t0, c) + x3, c = bits.Add64(x3, t1, c) + x5, t0 := bits.Mul64(f4h, y.arr[2]) + x4, c = bits.Add64(x4, t0, c) + x5, _ = bits.Add64(x5, 0, c) + t1, t0 = bits.Mul64(d4l, y.arr[2]) + x1, c = bits.Add64(x1, t0, 0) + x2, c = bits.Add64(x2, t1, c) + t1, t0 = bits.Mul64(f4l, y.arr[2]) + x3, c = bits.Add64(x3, t0, c) + x4, c = bits.Add64(x4, t1, c) + x5, _ = bits.Add64(x5, 0, c) + + t1, t0 = bits.Mul64(a4h, y.arr[3]) + x1, c = bits.Add64(x1, t0, 0) + x2, c = bits.Add64(x2, t1, c) + t1, t0 = bits.Mul64(d4h, y.arr[3]) + x3, c = bits.Add64(x3, t0, c) + x4, c = bits.Add64(x4, t1, c) + x6, t0 := bits.Mul64(f4h, y.arr[3]) + x5, c = bits.Add64(x5, t0, c) + x6, _ = bits.Add64(x6, 0, c) + t1, t0 = bits.Mul64(a4l, y.arr[3]) + x0, c = bits.Add64(x0, t0, 0) + x1, c = bits.Add64(x1, t1, c) + t1, t0 = bits.Mul64(d4l, y.arr[3]) + x2, c = bits.Add64(x2, t0, c) + x3, c = bits.Add64(x3, t1, c) + t1, t0 = bits.Mul64(f4l, y.arr[3]) + x4, c = bits.Add64(x4, t0, c) + x5, c = bits.Add64(x5, t1, c) + x6, _ = bits.Add64(x6, 0, c) + + // subtract + _, b = bits.Sub64(0, x0, 0) + _, b = bits.Sub64(0, x1, b) + r4l, b := bits.Sub64(0, x2, b) + r4k, b := bits.Sub64(0, x3, b) + r4j, b := bits.Sub64(r3l, x4, b) + r4i, b := bits.Sub64(r3m, x5, b) + r4h, _ := bits.Sub64(r3h, x6, b) + + // Multiply candidate for 1/4y by y, with full precision + + x0 = r4l + x1 = r4k + x2 = r4j + x3 = r4i + x4 = r4h + + q1, q0 = bits.Mul64(x0, y.arr[0]) + q3, q2 = bits.Mul64(x2, y.arr[0]) + q5, q4 := bits.Mul64(x4, y.arr[0]) + + t1, t0 = bits.Mul64(x1, y.arr[0]) + q1, c = bits.Add64(q1, t0, 0) + q2, c = bits.Add64(q2, t1, c) + t1, t0 = bits.Mul64(x3, y.arr[0]) + q3, c = bits.Add64(q3, t0, c) + q4, c = bits.Add64(q4, t1, c) + q5, _ = bits.Add64(q5, 0, c) + + t1, t0 = bits.Mul64(x0, y.arr[1]) + q1, c = bits.Add64(q1, t0, 0) + q2, c = bits.Add64(q2, t1, c) + t1, t0 = bits.Mul64(x2, y.arr[1]) + q3, c = bits.Add64(q3, t0, c) + q4, c = bits.Add64(q4, t1, c) + q6, t0 := bits.Mul64(x4, y.arr[1]) + q5, c = bits.Add64(q5, t0, c) + q6, _ = bits.Add64(q6, 0, c) + + t1, t0 = bits.Mul64(x1, y.arr[1]) + q2, c = bits.Add64(q2, t0, 0) + q3, c = bits.Add64(q3, t1, c) + t1, t0 = bits.Mul64(x3, y.arr[1]) + q4, c = bits.Add64(q4, t0, c) + q5, c = bits.Add64(q5, t1, c) + q6, _ = bits.Add64(q6, 0, c) + + t1, t0 = bits.Mul64(x0, y.arr[2]) + q2, c = bits.Add64(q2, t0, 0) + q3, c = bits.Add64(q3, t1, c) + t1, t0 = bits.Mul64(x2, y.arr[2]) + q4, c = bits.Add64(q4, t0, c) + q5, c = bits.Add64(q5, t1, c) + q7, t0 := bits.Mul64(x4, y.arr[2]) + q6, c = bits.Add64(q6, t0, c) + q7, _ = bits.Add64(q7, 0, c) + + t1, t0 = bits.Mul64(x1, y.arr[2]) + q3, c = bits.Add64(q3, t0, 0) + q4, c = bits.Add64(q4, t1, c) + t1, t0 = bits.Mul64(x3, y.arr[2]) + q5, c = bits.Add64(q5, t0, c) + q6, c = bits.Add64(q6, t1, c) + q7, _ = bits.Add64(q7, 0, c) + + t1, t0 = bits.Mul64(x0, y.arr[3]) + q3, c = bits.Add64(q3, t0, 0) + q4, c = bits.Add64(q4, t1, c) + t1, t0 = bits.Mul64(x2, y.arr[3]) + q5, c = bits.Add64(q5, t0, c) + q6, c = bits.Add64(q6, t1, c) + q8, t0 := bits.Mul64(x4, y.arr[3]) + q7, c = bits.Add64(q7, t0, c) + q8, _ = bits.Add64(q8, 0, c) + + t1, t0 = bits.Mul64(x1, y.arr[3]) + q4, c = bits.Add64(q4, t0, 0) + q5, c = bits.Add64(q5, t1, c) + t1, t0 = bits.Mul64(x3, y.arr[3]) + q6, c = bits.Add64(q6, t0, c) + q7, c = bits.Add64(q7, t1, c) + q8, _ = bits.Add64(q8, 0, c) + + // Final adjustment + + // subtract q from 1/4 + _, b = bits.Sub64(0, q0, 0) + _, b = bits.Sub64(0, q1, b) + _, b = bits.Sub64(0, q2, b) + _, b = bits.Sub64(0, q3, b) + _, b = bits.Sub64(0, q4, b) + _, b = bits.Sub64(0, q5, b) + _, b = bits.Sub64(0, q6, b) + _, b = bits.Sub64(0, q7, b) + _, b = bits.Sub64(uint64(1)<<62, q8, b) + + // decrement the result + x0, t := bits.Sub64(r4l, 1, 0) + x1, t = bits.Sub64(r4k, 0, t) + x2, t = bits.Sub64(r4j, 0, t) + x3, t = bits.Sub64(r4i, 0, t) + x4, _ = bits.Sub64(r4h, 0, t) + + // commit the decrement if the subtraction underflowed (reciprocal was too large) + if b != 0 { + r4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0 + } + + // Shift to correct bit alignment, truncating excess bits + + p = (p & 63) - 1 + + x0, c = bits.Add64(r4l, r4l, 0) + x1, c = bits.Add64(r4k, r4k, c) + x2, c = bits.Add64(r4j, r4j, c) + x3, c = bits.Add64(r4i, r4i, c) + x4, _ = bits.Add64(r4h, r4h, c) + + if p < 0 { + r4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0 + p = 0 // avoid negative shift below + } + + { + r := uint(p) // right shift + l := uint(64 - r) // left shift + + x0 = (r4l >> r) | (r4k << l) + x1 = (r4k >> r) | (r4j << l) + x2 = (r4j >> r) | (r4i << l) + x3 = (r4i >> r) | (r4h << l) + x4 = (r4h >> r) + } + + if p > 0 { + r4h, r4i, r4j, r4k, r4l = x4, x3, x2, x1, x0 + } + + mu[0] = r4l + mu[1] = r4k + mu[2] = r4j + mu[3] = r4i + mu[4] = r4h + + return mu +} + +// reduce4 computes the least non-negative residue of x modulo m +// +// requires a four-word modulus (m.arr[3] > 1) and its inverse (mu) +func reduce4(x [8]uint64, m *Uint, mu [5]uint64) (z Uint) { + + // NB: Most variable names in the comments match the pseudocode for + // Barrett reduction in the Handbook of Applied Cryptography. + + // q1 = x/2^192 + + x0 := x[3] + x1 := x[4] + x2 := x[5] + x3 := x[6] + x4 := x[7] + + // q2 = q1 * mu; q3 = q2 / 2^320 + + var q0, q1, q2, q3, q4, q5, t0, t1, c uint64 + + q0, _ = bits.Mul64(x3, mu[0]) + q1, t0 = bits.Mul64(x4, mu[0]) + q0, c = bits.Add64(q0, t0, 0) + q1, _ = bits.Add64(q1, 0, c) + + t1, _ = bits.Mul64(x2, mu[1]) + q0, c = bits.Add64(q0, t1, 0) + q2, t0 = bits.Mul64(x4, mu[1]) + q1, c = bits.Add64(q1, t0, c) + q2, _ = bits.Add64(q2, 0, c) + + t1, t0 = bits.Mul64(x3, mu[1]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + q2, _ = bits.Add64(q2, 0, c) + + t1, t0 = bits.Mul64(x2, mu[2]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + q3, t0 = bits.Mul64(x4, mu[2]) + q2, c = bits.Add64(q2, t0, c) + q3, _ = bits.Add64(q3, 0, c) + + t1, _ = bits.Mul64(x1, mu[2]) + q0, c = bits.Add64(q0, t1, 0) + t1, t0 = bits.Mul64(x3, mu[2]) + q1, c = bits.Add64(q1, t0, c) + q2, c = bits.Add64(q2, t1, c) + q3, _ = bits.Add64(q3, 0, c) + + t1, _ = bits.Mul64(x0, mu[3]) + q0, c = bits.Add64(q0, t1, 0) + t1, t0 = bits.Mul64(x2, mu[3]) + q1, c = bits.Add64(q1, t0, c) + q2, c = bits.Add64(q2, t1, c) + q4, t0 = bits.Mul64(x4, mu[3]) + q3, c = bits.Add64(q3, t0, c) + q4, _ = bits.Add64(q4, 0, c) + + t1, t0 = bits.Mul64(x1, mu[3]) + q0, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + t1, t0 = bits.Mul64(x3, mu[3]) + q2, c = bits.Add64(q2, t0, c) + q3, c = bits.Add64(q3, t1, c) + q4, _ = bits.Add64(q4, 0, c) + + t1, t0 = bits.Mul64(x0, mu[4]) + _, c = bits.Add64(q0, t0, 0) + q1, c = bits.Add64(q1, t1, c) + t1, t0 = bits.Mul64(x2, mu[4]) + q2, c = bits.Add64(q2, t0, c) + q3, c = bits.Add64(q3, t1, c) + q5, t0 = bits.Mul64(x4, mu[4]) + q4, c = bits.Add64(q4, t0, c) + q5, _ = bits.Add64(q5, 0, c) + + t1, t0 = bits.Mul64(x1, mu[4]) + q1, c = bits.Add64(q1, t0, 0) + q2, c = bits.Add64(q2, t1, c) + t1, t0 = bits.Mul64(x3, mu[4]) + q3, c = bits.Add64(q3, t0, c) + q4, c = bits.Add64(q4, t1, c) + q5, _ = bits.Add64(q5, 0, c) + + // Drop the fractional part of q3 + + q0 = q1 + q1 = q2 + q2 = q3 + q3 = q4 + q4 = q5 + + // r1 = x mod 2^320 + + x0 = x[0] + x1 = x[1] + x2 = x[2] + x3 = x[3] + x4 = x[4] + + // r2 = q3 * m mod 2^320 + + var r0, r1, r2, r3, r4 uint64 + + r4, r3 = bits.Mul64(q0, m.arr[3]) + _, t0 = bits.Mul64(q1, m.arr[3]) + r4, _ = bits.Add64(r4, t0, 0) + + t1, r2 = bits.Mul64(q0, m.arr[2]) + r3, c = bits.Add64(r3, t1, 0) + _, t0 = bits.Mul64(q2, m.arr[2]) + r4, _ = bits.Add64(r4, t0, c) + + t1, t0 = bits.Mul64(q1, m.arr[2]) + r3, c = bits.Add64(r3, t0, 0) + r4, _ = bits.Add64(r4, t1, c) + + t1, r1 = bits.Mul64(q0, m.arr[1]) + r2, c = bits.Add64(r2, t1, 0) + t1, t0 = bits.Mul64(q2, m.arr[1]) + r3, c = bits.Add64(r3, t0, c) + r4, _ = bits.Add64(r4, t1, c) + + t1, t0 = bits.Mul64(q1, m.arr[1]) + r2, c = bits.Add64(r2, t0, 0) + r3, c = bits.Add64(r3, t1, c) + _, t0 = bits.Mul64(q3, m.arr[1]) + r4, _ = bits.Add64(r4, t0, c) + + t1, r0 = bits.Mul64(q0, m.arr[0]) + r1, c = bits.Add64(r1, t1, 0) + t1, t0 = bits.Mul64(q2, m.arr[0]) + r2, c = bits.Add64(r2, t0, c) + r3, c = bits.Add64(r3, t1, c) + _, t0 = bits.Mul64(q4, m.arr[0]) + r4, _ = bits.Add64(r4, t0, c) + + t1, t0 = bits.Mul64(q1, m.arr[0]) + r1, c = bits.Add64(r1, t0, 0) + r2, c = bits.Add64(r2, t1, c) + t1, t0 = bits.Mul64(q3, m.arr[0]) + r3, c = bits.Add64(r3, t0, c) + r4, _ = bits.Add64(r4, t1, c) + + // r = r1 - r2 + + var b uint64 + + r0, b = bits.Sub64(x0, r0, 0) + r1, b = bits.Sub64(x1, r1, b) + r2, b = bits.Sub64(x2, r2, b) + r3, b = bits.Sub64(x3, r3, b) + r4, b = bits.Sub64(x4, r4, b) + + // if r<0 then r+=m + + if b != 0 { + r0, c = bits.Add64(r0, m.arr[0], 0) + r1, c = bits.Add64(r1, m.arr[1], c) + r2, c = bits.Add64(r2, m.arr[2], c) + r3, c = bits.Add64(r3, m.arr[3], c) + r4, _ = bits.Add64(r4, 0, c) + } + + // while (r>=m) r-=m + + for { + // q = r - m + q0, b = bits.Sub64(r0, m.arr[0], 0) + q1, b = bits.Sub64(r1, m.arr[1], b) + q2, b = bits.Sub64(r2, m.arr[2], b) + q3, b = bits.Sub64(r3, m.arr[3], b) + q4, b = bits.Sub64(r4, 0, b) + + // if borrow break + if b != 0 { + break + } + + // r = q + r4, r3, r2, r1, r0 = q4, q3, q2, q1, q0 + } + + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = r3, r2, r1, r0 + + return z +} diff --git a/examples/gno.land/p/demo/uint256/uint256.gno b/examples/gno.land/p/demo/uint256/uint256.gno new file mode 100644 index 00000000000..0f8b346d526 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/uint256.gno @@ -0,0 +1,1264 @@ +// Ported from https://github.com/holiman/uint256 +package uint256 + +import ( + "errors" + "math/bits" + "strconv" +) + +const ( + MaxUint64 = 1<<64 - 1 + uintSize = 32 << (^uint(0) >> 63) +) + +func Zero() *Uint { + return NewUint(0) +} + +func One() *Uint { + return NewUint(1) +} + +func (x *Uint) Min(y *Uint) *Uint { + if x.Lt(y) { + return x + } + return y +} + +// Uint is represented as an array of 4 uint64, in little-endian order, +// so that Uint[3] is the most significant, and Uint[0] is the least significant +type Uint struct { + arr [4]uint64 +} + +// NewUint returns a new initialized Uint. +func NewUint(val uint64) *Uint { + z := &Uint{arr: [4]uint64{val, 0, 0, 0}} + return z +} + +// Uint64 returns the lower 64-bits of z +func (z *Uint) Uint64() uint64 { + return z.arr[0] +} + +// Uint64WithOverflow returns the lower 64-bits of z and bool whether overflow occurred +func (z *Uint) Uint64WithOverflow() (uint64, bool) { + return z.arr[0], (z.arr[1] | z.arr[2] | z.arr[3]) != 0 +} + +func (z *Uint) Int32() int32 { + x := z.arr[0] + if x > 0x7fffffff { + panic("U256 Int32 overflow") + } + return int32(x) +} + +// SetUint64 sets z to the value x +func (z *Uint) SetUint64(x uint64) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x + return z +} + +// IsUint64 reports whether z can be represented as a uint64. +func (z *Uint) IsUint64() bool { + return (z.arr[1] | z.arr[2] | z.arr[3]) == 0 +} + +// IsZero returns true if z == 0 +func (z *Uint) IsZero() bool { + return (z.arr[0] | z.arr[1] | z.arr[2] | z.arr[3]) == 0 +} + +// Clear sets z to 0 +func (z *Uint) Clear() *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 0 + return z +} + +// Neg returns -x mod 2**256. +func (z *Uint) Neg(x *Uint) *Uint { + return z.Sub(new(Uint), x) +} + +// SetAllOne sets all the bits of z to 1 +func (z *Uint) SetAllOne() *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, MaxUint64 + return z +} + +// Not sets z = ^x and returns z. +func (z *Uint) Not(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = ^x.arr[3], ^x.arr[2], ^x.arr[1], ^x.arr[0] + return z +} + +// Lt returns true if z < x +func (z *Uint) Lt(x *Uint) bool { + // z < x <=> z - x < 0 i.e. when subtraction overflows. + _, carry := bits.Sub64(z.arr[0], x.arr[0], 0) + _, carry = bits.Sub64(z.arr[1], x.arr[1], carry) + _, carry = bits.Sub64(z.arr[2], x.arr[2], carry) + _, carry = bits.Sub64(z.arr[3], x.arr[3], carry) + + return carry != 0 +} + +// Gt returns true if z > x +func (z *Uint) Gt(x *Uint) bool { + return x.Lt(z) +} + +// Lte returns true if z <= x +func (z *Uint) Lte(x *Uint) bool { + cond1 := z.Lt(x) + cond2 := z.Eq(x) + + if cond1 || cond2 { + return true + } + return false +} + +// Gte returns true if z >= x +func (z *Uint) Gte(x *Uint) bool { + cond1 := z.Gt(x) + cond2 := z.Eq(x) + + if cond1 || cond2 { + return true + } + return false +} + +// Eq returns true if z == x +func (z *Uint) Eq(x *Uint) bool { + return (z.arr[0] == x.arr[0]) && (z.arr[1] == x.arr[1]) && (z.arr[2] == x.arr[2]) && (z.arr[3] == x.arr[3]) +} + +// Cmp compares z and x and returns: +// +// -1 if z < x +// 0 if z == x +// +1 if z > x +func (z *Uint) Cmp(x *Uint) (r int) { + // z < x <=> z - x < 0 i.e. when subtraction overflows. + d0, carry := bits.Sub64(z.arr[0], x.arr[0], 0) + d1, carry := bits.Sub64(z.arr[1], x.arr[1], carry) + d2, carry := bits.Sub64(z.arr[2], x.arr[2], carry) + d3, carry := bits.Sub64(z.arr[3], x.arr[3], carry) + if carry == 1 { + return -1 + } + if d0|d1|d2|d3 == 0 { + return 0 + } + return 1 +} + +// Set sets z to x and returns z. +func (z *Uint) Set(x *Uint) *Uint { + *z = *x + + return z +} + +// SetOne sets z to 1 +func (z *Uint) SetOne() *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 1 + return z +} + +// Add sets z to the sum x+y +func (z *Uint) Add(x, y *Uint) *Uint { + var carry uint64 + z.arr[0], carry = bits.Add64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Add64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Add64(x.arr[2], y.arr[2], carry) + z.arr[3], _ = bits.Add64(x.arr[3], y.arr[3], carry) + return z +} + +// AddOverflow sets z to the sum x+y, and returns z and whether overflow occurred +func (z *Uint) AddOverflow(x, y *Uint) (*Uint, bool) { + var carry uint64 + z.arr[0], carry = bits.Add64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Add64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Add64(x.arr[2], y.arr[2], carry) + z.arr[3], carry = bits.Add64(x.arr[3], y.arr[3], carry) + return z, carry != 0 +} + +// SubOverflow sets z to the difference x-y and returns z and true if the operation underflowed +func (z *Uint) SubOverflow(x, y *Uint) (*Uint, bool) { + var carry uint64 + z.arr[0], carry = bits.Sub64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Sub64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Sub64(x.arr[2], y.arr[2], carry) + z.arr[3], carry = bits.Sub64(x.arr[3], y.arr[3], carry) + return z, carry != 0 +} + +// Sub sets z to the difference x-y +func (z *Uint) Sub(x, y *Uint) *Uint { + var carry uint64 + z.arr[0], carry = bits.Sub64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Sub64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Sub64(x.arr[2], y.arr[2], carry) + z.arr[3], _ = bits.Sub64(x.arr[3], y.arr[3], carry) + return z +} + +// commented out for possible overflow +// Mul sets z to the product x*y +func (z *Uint) Mul(x, y *Uint) *Uint { + var ( + res Uint + carry uint64 + res1, res2, res3 uint64 + ) + + carry, res.arr[0] = bits.Mul64(x.arr[0], y.arr[0]) + carry, res1 = umulHop(carry, x.arr[1], y.arr[0]) + carry, res2 = umulHop(carry, x.arr[2], y.arr[0]) + res3 = x.arr[3]*y.arr[0] + carry + + carry, res.arr[1] = umulHop(res1, x.arr[0], y.arr[1]) + carry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry) + res3 = res3 + x.arr[2]*y.arr[1] + carry + + carry, res.arr[2] = umulHop(res2, x.arr[0], y.arr[2]) + res3 = res3 + x.arr[1]*y.arr[2] + carry + + res.arr[3] = res3 + x.arr[0]*y.arr[3] + + return z.Set(&res) +} + +// MulOverflow sets z to the product x*y, and returns z and whether overflow occurred +func (z *Uint) MulOverflow(x, y *Uint) (*Uint, bool) { + p := umul(x, y) + copy(z.arr[:], p[:4]) + return z, (p[4] | p[5] | p[6] | p[7]) != 0 +} + +// MulMod calculates the modulo-m multiplication of x and y and +// returns z. +// If m == 0, z is set to 0 (OBS: differs from the big.Int) +func (z *Uint) MulMod(x, y, m *Uint) *Uint { + if x.IsZero() || y.IsZero() || m.IsZero() { + return z.Clear() + } + p := umul(x, y) + + if m.arr[3] != 0 { + mu := Reciprocal(m) + r := reduce4(p, m, mu) + return z.Set(&r) + } + + var ( + pl Uint + ph Uint + ) + + pl = Uint{arr: [4]uint64{p[0], p[1], p[2], p[3]}} + ph = Uint{arr: [4]uint64{p[4], p[5], p[6], p[7]}} + + // If the multiplication is within 256 bits use Mod(). + if ph.IsZero() { + return z.Mod(&pl, m) + } + + var quot [8]uint64 + rem := udivrem(quot[:], p[:], m) + return z.Set(&rem) +} + +// umulStep computes (hi * 2^64 + lo) = z + (x * y) + carry. +func umulStep(z, x, y, carry uint64) (hi, lo uint64) { + hi, lo = bits.Mul64(x, y) + lo, carry = bits.Add64(lo, carry, 0) + hi, _ = bits.Add64(hi, 0, carry) + lo, carry = bits.Add64(lo, z, 0) + hi, _ = bits.Add64(hi, 0, carry) + return hi, lo +} + +// umulHop computes (hi * 2^64 + lo) = z + (x * y) +func umulHop(z, x, y uint64) (hi, lo uint64) { + hi, lo = bits.Mul64(x, y) + lo, carry := bits.Add64(lo, z, 0) + hi, _ = bits.Add64(hi, 0, carry) + return hi, lo +} + +// umul computes full 256 x 256 -> 512 multiplication. +func umul(x, y *Uint) [8]uint64 { + var ( + res [8]uint64 + carry, carry4, carry5, carry6 uint64 + res1, res2, res3, res4, res5 uint64 + ) + + carry, res[0] = bits.Mul64(x.arr[0], y.arr[0]) + carry, res1 = umulHop(carry, x.arr[1], y.arr[0]) + carry, res2 = umulHop(carry, x.arr[2], y.arr[0]) + carry4, res3 = umulHop(carry, x.arr[3], y.arr[0]) + + carry, res[1] = umulHop(res1, x.arr[0], y.arr[1]) + carry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry) + carry, res3 = umulStep(res3, x.arr[2], y.arr[1], carry) + carry5, res4 = umulStep(carry4, x.arr[3], y.arr[1], carry) + + carry, res[2] = umulHop(res2, x.arr[0], y.arr[2]) + carry, res3 = umulStep(res3, x.arr[1], y.arr[2], carry) + carry, res4 = umulStep(res4, x.arr[2], y.arr[2], carry) + carry6, res5 = umulStep(carry5, x.arr[3], y.arr[2], carry) + + carry, res[3] = umulHop(res3, x.arr[0], y.arr[3]) + carry, res[4] = umulStep(res4, x.arr[1], y.arr[3], carry) + carry, res[5] = umulStep(res5, x.arr[2], y.arr[3], carry) + res[7], res[6] = umulStep(carry6, x.arr[3], y.arr[3], carry) + + return res +} + +// commented out for possible overflow +// Div sets z to the quotient x/y for returns z. +// If y == 0, z is set to 0 +func (z *Uint) Div(x, y *Uint) *Uint { + if y.IsZero() || y.Gt(x) { + return z.Clear() + } + if x.Eq(y) { + return z.SetOne() + } + // Shortcut some cases + if x.IsUint64() { + return z.SetUint64(x.Uint64() / y.Uint64()) + } + + // At this point, we know + // x/y ; x > y > 0 + + var quot Uint + udivrem(quot.arr[:], x.arr[:], y) + return z.Set(") +} + +// udivrem divides u by d and produces both quotient and remainder. +// The quotient is stored in provided quot - len(u)-len(d)+1 words. +// It loosely follows the Knuth's division algorithm (sometimes referenced as "schoolbook" division) using 64-bit words. +// See Knuth, Volume 2, section 4.3.1, Algorithm D. +func udivrem(quot, u []uint64, d *Uint) (rem Uint) { + var dLen int + for i := len(d.arr) - 1; i >= 0; i-- { + if d.arr[i] != 0 { + dLen = i + 1 + break + } + } + + shift := uint(bits.LeadingZeros64(d.arr[dLen-1])) + + var dnStorage Uint + dn := dnStorage.arr[:dLen] + for i := dLen - 1; i > 0; i-- { + dn[i] = (d.arr[i] << shift) | (d.arr[i-1] >> (64 - shift)) + } + dn[0] = d.arr[0] << shift + + var uLen int + for i := len(u) - 1; i >= 0; i-- { + if u[i] != 0 { + uLen = i + 1 + break + } + } + + if uLen < dLen { + copy(rem.arr[:], u) + return rem + } + + var unStorage [9]uint64 + un := unStorage[:uLen+1] + un[uLen] = u[uLen-1] >> (64 - shift) + for i := uLen - 1; i > 0; i-- { + un[i] = (u[i] << shift) | (u[i-1] >> (64 - shift)) + } + un[0] = u[0] << shift + + // TODO: Skip the highest word of numerator if not significant. + + if dLen == 1 { + r := udivremBy1(quot, un, dn[0]) + rem.SetUint64(r >> shift) + return rem + } + + udivremKnuth(quot, un, dn) + + for i := 0; i < dLen-1; i++ { + rem.arr[i] = (un[i] >> shift) | (un[i+1] << (64 - shift)) + } + rem.arr[dLen-1] = un[dLen-1] >> shift + + return rem +} + +// udivremBy1 divides u by single normalized word d and produces both quotient and remainder. +// The quotient is stored in provided quot. +func udivremBy1(quot, u []uint64, d uint64) (rem uint64) { + reciprocal := reciprocal2by1(d) + rem = u[len(u)-1] // Set the top word as remainder. + for j := len(u) - 2; j >= 0; j-- { + quot[j], rem = udivrem2by1(rem, u[j], d, reciprocal) + } + return rem +} + +// udivremKnuth implements the division of u by normalized multiple word d from the Knuth's division algorithm. +// The quotient is stored in provided quot - len(u)-len(d) words. +// Updates u to contain the remainder - len(d) words. +func udivremKnuth(quot, u, d []uint64) { + dh := d[len(d)-1] + dl := d[len(d)-2] + reciprocal := reciprocal2by1(dh) + + for j := len(u) - len(d) - 1; j >= 0; j-- { + u2 := u[j+len(d)] + u1 := u[j+len(d)-1] + u0 := u[j+len(d)-2] + + var qhat, rhat uint64 + if u2 >= dh { // Division overflows. + qhat = ^uint64(0) + // TODO: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case). + } else { + qhat, rhat = udivrem2by1(u2, u1, dh, reciprocal) + ph, pl := bits.Mul64(qhat, dl) + if ph > rhat || (ph == rhat && pl > u0) { + qhat-- + // TODO: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case). + } + } + + // Multiply and subtract. + borrow := subMulTo(u[j:], d, qhat) + u[j+len(d)] = u2 - borrow + if u2 < borrow { // Too much subtracted, add back. + qhat-- + u[j+len(d)] += addTo(u[j:], d) + } + + quot[j] = qhat // Store quotient digit. + } +} + +// isBitSet returns true if bit n-th is set, where n = 0 is LSB. +// The n must be <= 255. +func (z *Uint) isBitSet(n uint) bool { + return (z.arr[n/64] & (1 << (n % 64))) != 0 +} + +// addTo computes x += y. +// Requires len(x) >= len(y). +func addTo(x, y []uint64) uint64 { + var carry uint64 + for i := 0; i < len(y); i++ { + x[i], carry = bits.Add64(x[i], y[i], carry) + } + return carry +} + +// subMulTo computes x -= y * multiplier. +// Requires len(x) >= len(y). +func subMulTo(x, y []uint64, multiplier uint64) uint64 { + var borrow uint64 + for i := 0; i < len(y); i++ { + s, carry1 := bits.Sub64(x[i], borrow, 0) + ph, pl := bits.Mul64(y[i], multiplier) + t, carry2 := bits.Sub64(s, pl, 0) + x[i] = t + borrow = ph + carry1 + carry2 + } + return borrow +} + +// reciprocal2by1 computes <^d, ^0> / d. +func reciprocal2by1(d uint64) uint64 { + reciprocal, _ := bits.Div64(^d, ^uint64(0), d) + return reciprocal +} + +// udivrem2by1 divides / d and produces both quotient and remainder. +// It uses the provided d's reciprocal. +// Implementation ported from https://github.com/chfast/intx and is based on +// "Improved division by invariant integers", Algorithm 4. +func udivrem2by1(uh, ul, d, reciprocal uint64) (quot, rem uint64) { + qh, ql := bits.Mul64(reciprocal, uh) + ql, carry := bits.Add64(ql, ul, 0) + qh, _ = bits.Add64(qh, uh, carry) + qh++ + + r := ul - qh*d + + if r > ql { + qh-- + r += d + } + + if r >= d { + qh++ + r -= d + } + + return qh, r +} + +// Lsh sets z = x << n and returns z. +func (z *Uint) Lsh(x *Uint, n uint) *Uint { + // n % 64 == 0 + if n&0x3f == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.lsh64(x) + case 128: + return z.lsh128(x) + case 192: + return z.lsh192(x) + default: + return z.Clear() + } + } + var a, b uint64 + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.Clear() + } + z.lsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.lsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.lsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + a = z.arr[0] >> (64 - n) + z.arr[0] = z.arr[0] << n + +sh64: + b = z.arr[1] >> (64 - n) + z.arr[1] = (z.arr[1] << n) | a + +sh128: + a = z.arr[2] >> (64 - n) + z.arr[2] = (z.arr[2] << n) | b + +sh192: + z.arr[3] = (z.arr[3] << n) | a + + return z +} + +// Rsh sets z = x >> n and returns z. +func (z *Uint) Rsh(x *Uint, n uint) *Uint { + // n % 64 == 0 + if n&0x3f == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.rsh64(x) + case 128: + return z.rsh128(x) + case 192: + return z.rsh192(x) + default: + return z.Clear() + } + } + var a, b uint64 + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.Clear() + } + z.rsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.rsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.rsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + a = z.arr[3] << (64 - n) + z.arr[3] = z.arr[3] >> n + +sh64: + b = z.arr[2] << (64 - n) + z.arr[2] = (z.arr[2] >> n) | a + +sh128: + a = z.arr[1] << (64 - n) + z.arr[1] = (z.arr[1] >> n) | b + +sh192: + z.arr[0] = (z.arr[0] >> n) | a + + return z +} + +// SRsh (Signed/Arithmetic right shift) +// considers z to be a signed integer, during right-shift +// and sets z = x >> n and returns z. +func (z *Uint) SRsh(x *Uint, n uint) *Uint { + // If the MSB is 0, SRsh is same as Rsh. + if !x.isBitSet(255) { + return z.Rsh(x, n) + } + if n%64 == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.srsh64(x) + case 128: + return z.srsh128(x) + case 192: + return z.srsh192(x) + default: + return z.SetAllOne() + } + } + var a uint64 = MaxUint64 << (64 - n%64) + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.SetAllOne() + } + z.srsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.srsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.srsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + z.arr[3], a = (z.arr[3]>>n)|a, z.arr[3]<<(64-n) + +sh64: + z.arr[2], a = (z.arr[2]>>n)|a, z.arr[2]<<(64-n) + +sh128: + z.arr[1], a = (z.arr[1]>>n)|a, z.arr[1]<<(64-n) + +sh192: + z.arr[0] = (z.arr[0] >> n) | a + + return z +} + +func (z *Uint) lsh64(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[2], x.arr[1], x.arr[0], 0 + return z +} + +func (z *Uint) lsh128(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[1], x.arr[0], 0, 0 + return z +} + +func (z *Uint) lsh192(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[0], 0, 0, 0 + return z +} + +func (z *Uint) rsh64(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, x.arr[3], x.arr[2], x.arr[1] + return z +} + +func (z *Uint) rsh128(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, x.arr[3], x.arr[2] + return z +} + +func (z *Uint) rsh192(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x.arr[3] + return z +} + +func (z *Uint) srsh64(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, x.arr[3], x.arr[2], x.arr[1] + return z +} + +func (z *Uint) srsh128(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, x.arr[3], x.arr[2] + return z +} + +func (z *Uint) srsh192(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, x.arr[3] + return z +} + +// Or sets z = x | y and returns z. +func (z *Uint) Or(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] | y.arr[0] + z.arr[1] = x.arr[1] | y.arr[1] + z.arr[2] = x.arr[2] | y.arr[2] + z.arr[3] = x.arr[3] | y.arr[3] + return z +} + +// And sets z = x & y and returns z. +func (z *Uint) And(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] & y.arr[0] + z.arr[1] = x.arr[1] & y.arr[1] + z.arr[2] = x.arr[2] & y.arr[2] + z.arr[3] = x.arr[3] & y.arr[3] + return z +} + +// Xor sets z = x ^ y and returns z. +func (z *Uint) Xor(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] ^ y.arr[0] + z.arr[1] = x.arr[1] ^ y.arr[1] + z.arr[2] = x.arr[2] ^ y.arr[2] + z.arr[3] = x.arr[3] ^ y.arr[3] + return z +} + +// MarshalJSON implements json.Marshaler. +// MarshalJSON marshals using the 'decimal string' representation. This is _not_ compatible +// with big.Uint: big.Uint marshals into JSON 'native' numeric format. +// +// The JSON native format is, on some platforms, (e.g. javascript), limited to 53-bit large +// integer space. Thus, U256 uses string-format, which is not compatible with +// big.int (big.Uint refuses to unmarshal a string representation). +func (z *Uint) MarshalJSON() ([]byte, error) { + return []byte(`"` + z.Dec() + `"`), nil +} + +// UnmarshalJSON implements json.Unmarshaler. UnmarshalJSON accepts either +// - Quoted string: either hexadecimal OR decimal +// - Not quoted string: only decimal +func (z *Uint) UnmarshalJSON(input []byte) error { + if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { + // if not quoted, it must be decimal + return z.fromDecimal(string(input)) + } + return z.UnmarshalText(input[1 : len(input)-1]) +} + +// MarshalText implements encoding.TextMarshaler +// MarshalText marshals using the decimal representation (compatible with big.Uint) +func (z *Uint) MarshalText() ([]byte, error) { + return []byte(z.Dec()), nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. This method +// can unmarshal either hexadecimal or decimal. +// - For hexadecimal, the input _must_ be prefixed with 0x or 0X +func (z *Uint) UnmarshalText(input []byte) error { + if len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') { + return z.fromHex(string(input)) + } + return z.fromDecimal(string(input)) +} + +const ( + hextable = "0123456789abcdef" + bintable = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x06\a\b\t\xff\xff\xff\xff\xff\xff\xff\n\v\f\r\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\n\v\f\r\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + badNibble = 0xff +) + +// fromHex is the internal implementation of parsing a hex-string. +func (z *Uint) fromHex(hex string) error { + if err := checkNumberS(hex); err != nil { + return err + } + if len(hex) > 66 { + return ErrBig256Range + } + z.Clear() + end := len(hex) + for i := 0; i < 4; i++ { + start := end - 16 + if start < 2 { + start = 2 + } + for ri := start; ri < end; ri++ { + nib := bintable[hex[ri]] + if nib == badNibble { + return ErrSyntax + } + z.arr[i] = z.arr[i] << 4 + z.arr[i] += uint64(nib) + } + end = start + } + return nil +} + +// FromDecimal is a convenience-constructor to create an Uint from a +// decimal (base 10) string. Numbers larger than 256 bits are not accepted. +func FromDecimal(decimal string) (*Uint, error) { + var z Uint + if err := z.SetFromDecimal(decimal); err != nil { + return nil, err + } + return &z, nil +} + +// MustFromDecimal is a convenience-constructor to create an Int from a +// decimal (base 10) string. +// Returns a new Int and panics if any error occurred. +func MustFromDecimal(decimal string) *Uint { + var z Uint + if err := z.SetFromDecimal(decimal); err != nil { + panic(err) + } + return &z +} + +const twoPow256Sub1 = "115792089237316195423570985008687907853269984665640564039457584007913129639935" + +// SetFromDecimal sets z from the given string, interpreted as a decimal number. +// OBS! This method is _not_ strictly identical to the (*big.Uint).SetString(..., 10) method. +// Notable differences: +// - This method does not accept underscore input, e.g. "100_000", +// - This method does not accept negative zero as valid, e.g "-0", +// - (this method does not accept any negative input as valid)) +func (z *Uint) SetFromDecimal(s string) (err error) { + // Remove max one leading + + if len(s) > 0 && s[0] == '+' { + s = s[1:] + } + // Remove any number of leading zeroes + if len(s) > 0 && s[0] == '0' { + var i int + var c rune + for i, c = range s { + if c != '0' { + break + } + } + s = s[i:] + } + if len(s) < len(twoPow256Sub1) { + return z.fromDecimal(s) + } + if len(s) == len(twoPow256Sub1) { + if s > twoPow256Sub1 { + return ErrBig256Range + } + return z.fromDecimal(s) + } + return ErrBig256Range +} + +var ( + ErrEmptyString = errors.New("empty hex string") + ErrSyntax = errors.New("invalid hex string") + ErrMissingPrefix = errors.New("hex string without 0x prefix") + ErrEmptyNumber = errors.New("hex string \"0x\"") + ErrLeadingZero = errors.New("hex number with leading zero digits") + ErrBig256Range = errors.New("hex number > 256 bits") + ErrBadBufferLength = errors.New("bad ssz buffer length") + ErrBadEncodedLength = errors.New("bad ssz encoded length") +) + +func checkNumberS(input string) error { + l := len(input) + if l == 0 { + return ErrEmptyString + } + if l < 2 || input[0] != '0' || + (input[1] != 'x' && input[1] != 'X') { + return ErrMissingPrefix + } + if l == 2 { + return ErrEmptyNumber + } + if len(input) > 3 && input[2] == '0' { + return ErrLeadingZero + } + return nil +} + +// multipliers holds the values that are needed for fromDecimal +var multipliers = [5]*Uint{ + nil, // represents first round, no multiplication needed + {[4]uint64{10000000000000000000, 0, 0, 0}}, // 10 ^ 19 + {[4]uint64{687399551400673280, 5421010862427522170, 0, 0}}, // 10 ^ 38 + {[4]uint64{5332261958806667264, 17004971331911604867, 2938735877055718769, 0}}, // 10 ^ 57 + {[4]uint64{0, 8607968719199866880, 532749306367912313, 1593091911132452277}}, // 10 ^ 76 +} + +// fromDecimal is a helper function to only ever be called via SetFromDecimal +// this function takes a string and chunks it up, calling ParseUint on it up to 5 times +// these chunks are then multiplied by the proper power of 10, then added together. +func (z *Uint) fromDecimal(bs string) error { + // first clear the input + z.Clear() + // the maximum value of uint64 is 18446744073709551615, which is 20 characters + // one less means that a string of 19 9's is always within the uint64 limit + var ( + num uint64 + err error + remaining = len(bs) + ) + if remaining == 0 { + return errors.New("EOF") + } + // We proceed in steps of 19 characters (nibbles), from least significant to most significant. + // This means that the first (up to) 19 characters do not need to be multiplied. + // In the second iteration, our slice of 19 characters needs to be multipleied + // by a factor of 10^19. Et cetera. + for i, mult := range multipliers { + if remaining <= 0 { + return nil // Done + } else if remaining > 19 { + num, err = parseUint(bs[remaining-19:remaining], 10, 64) + } else { + // Final round + num, err = parseUint(bs, 10, 64) + } + if err != nil { + return err + } + // add that number to our running total + if i == 0 { + z.SetUint64(num) + } else { + base := NewUint(num) + z.Add(z, base.Mul(base, mult)) + } + // Chop off another 19 characters + if remaining > 19 { + bs = bs[0 : remaining-19] + } + remaining -= 19 + } + return nil +} + +// lower(c) is a lower-case letter if and only if +// c is either that lower-case letter or the equivalent upper-case letter. +// Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'. +// Note that lower of non-letters can produce other non-letters. +func lower(c byte) byte { + return c | ('x' - 'X') +} + +// ParseUint is like ParseUint but for unsigned numbers. +// +// A sign prefix is not permitted. +func parseUint(s string, base int, bitSize int) (uint64, error) { + const fnParseUint = "ParseUint" + + if s == "" { + return 0, errors.New("syntax error: ParseUint empty string") + } + + base0 := base == 0 + + s0 := s + switch { + case 2 <= base && base <= 36: + // valid base; nothing to do + + case base == 0: + // Look for octal, hex prefix. + base = 10 + if s[0] == '0' { + switch { + case len(s) >= 3 && lower(s[1]) == 'b': + base = 2 + s = s[2:] + case len(s) >= 3 && lower(s[1]) == 'o': + base = 8 + s = s[2:] + case len(s) >= 3 && lower(s[1]) == 'x': + base = 16 + s = s[2:] + default: + base = 8 + s = s[1:] + } + } + + default: + return 0, errors.New("invalid base") + } + + if bitSize == 0 { + bitSize = uintSize + } else if bitSize < 0 || bitSize > 64 { + return 0, errors.New("invalid bit size") + } + + // Cutoff is the smallest number such that cutoff*base > maxUint64. + // Use compile-time constants for common cases. + var cutoff uint64 + switch base { + case 10: + cutoff = MaxUint64/10 + 1 + case 16: + cutoff = MaxUint64/16 + 1 + default: + cutoff = MaxUint64/uint64(base) + 1 + } + + maxVal := uint64(1)<= byte(base) { + return 0, errors.New("syntax error") + } + + if n >= cutoff { + // n*base overflows + return maxVal, errors.New("range error") + } + n *= uint64(base) + + n1 := n + uint64(d) + if n1 < n || n1 > maxVal { + // n+d overflows + return maxVal, errors.New("range error") + } + n = n1 + } + + if underscores && !underscoreOK(s0) { + return 0, errors.New("syntax error") + } + + return n, nil +} + +// underscoreOK reports whether the underscores in s are allowed. +// Checking them in this one function lets all the parsers skip over them simply. +// Underscore must appear only between digits or between a base prefix and a digit. +func underscoreOK(s string) bool { + // saw tracks the last character (class) we saw: + // ^ for beginning of number, + // 0 for a digit or base prefix, + // _ for an underscore, + // ! for none of the above. + saw := '^' + i := 0 + + // Optional sign. + if len(s) >= 1 && (s[0] == '-' || s[0] == '+') { + s = s[1:] + } + + // Optional base prefix. + hex := false + if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') { + i = 2 + saw = '0' // base prefix counts as a digit for "underscore as digit separator" + hex = lower(s[1]) == 'x' + } + + // Number proper. + for ; i < len(s); i++ { + // Digits are always okay. + if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' { + saw = '0' + continue + } + // Underscore must follow digit. + if s[i] == '_' { + if saw != '0' { + return false + } + saw = '_' + continue + } + // Underscore must also be followed by digit. + if saw == '_' { + return false + } + // Saw non-digit, non-underscore. + saw = '!' + } + return saw != '_' +} + +// Dec returns the decimal representation of z. +func (z *Uint) Dec() string { + if z.IsZero() { + return "0" + } + if z.IsUint64() { + return strconv.FormatUint(z.Uint64(), 10) + } + + // The max uint64 value being 18446744073709551615, the largest + // power-of-ten below that is 10000000000000000000. + // When we do a DivMod using that number, the remainder that we + // get back is the lower part of the output. + // + // The ascii-output of remainder will never exceed 19 bytes (since it will be + // below 10000000000000000000). + // + // Algorithm example using 100 as divisor + // + // 12345 % 100 = 45 (rem) + // 12345 / 100 = 123 (quo) + // -> output '45', continue iterate on 123 + var ( + // out is 98 bytes long: 78 (max size of a string without leading zeroes, + // plus slack so we can copy 19 bytes every iteration). + // We init it with zeroes, because when strconv appends the ascii representations, + // it will omit leading zeroes. + out = []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + divisor = NewUint(10000000000000000000) // 20 digits + y = new(Uint).Set(z) // copy to avoid modifying z + pos = len(out) // position to write to + buf = make([]byte, 0, 19) // buffer to write uint64:s to + ) + for { + // Obtain Q and R for divisor + var quot Uint + rem := udivrem(quot.arr[:], y.arr[:], divisor) + y.Set(") // Set Q for next loop + // Convert the R to ascii representation + buf = strconv.AppendUint(buf[:0], rem.Uint64(), 10) + // Copy in the ascii digits + copy(out[pos-len(buf):], buf) + if y.IsZero() { + break + } + // Move 19 digits left + pos -= 19 + } + // skip leading zeroes by only using the 'used size' of buf + return string(out[pos-len(buf):]) +} + +func (z *Uint) ToString() string { + if z == nil { + panic("U256 ToString() nil") + } + + return z.Dec() +} + +// 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.Uint) +func (z *Uint) Mod(x, y *Uint) *Uint { + if x.IsZero() || y.IsZero() { + return z.Clear() + } + switch x.Cmp(y) { + case -1: + // x < y + copy(z.arr[:], x.arr[:]) + return z + case 0: + // x == y + return z.Clear() // They are equal + } + + // At this point: + // x != 0 + // y != 0 + // x > y + + // Shortcut trivial case + if x.IsUint64() { + return z.SetUint64(x.Uint64() % y.Uint64()) + } + + var quot Uint + *z = udivrem(quot.arr[:], x.arr[:], y) + return z +} + +// Clone creates a new Int identical to z +func (z *Uint) Clone() *Uint { + var x Uint + x.arr[0] = z.arr[0] + x.arr[1] = z.arr[1] + x.arr[2] = z.arr[2] + x.arr[3] = z.arr[3] + + return &x +} + +func (z *Uint) IsNil() bool { + return z == nil +} + +func (z *Uint) NilToZero() *Uint { + if z == nil { + z = NewUint(0) + } + + return z +} + +func (z *Uint) AndNot(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] &^ y.arr[0] + z.arr[1] = x.arr[1] &^ y.arr[1] + z.arr[2] = x.arr[2] &^ y.arr[2] + z.arr[3] = x.arr[3] &^ y.arr[3] + return z +} diff --git a/examples/gno.land/p/demo/uint256/uint256_overflow_calculation.gno b/examples/gno.land/p/demo/uint256/uint256_overflow_calculation.gno new file mode 100644 index 00000000000..b4a9efebee5 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/uint256_overflow_calculation.gno @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: CC-BY-4.0 +// REF: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/FullMath.sol +package uint256 + +func fullMul( + x *Uint, + y *Uint, +) (*Uint, *Uint) { // l, h + mm := new(Uint).MulMod(x, y, MustFromDecimal(MAX_UINT256)) + + l := new(Uint).Mul(x, y) + h := new(Uint).Sub(mm, l) + + if mm.Lt(l) { + h = new(Uint).Sub(h, One()) + } + + return l, h +} + +func fullDiv( + l *Uint, + h *Uint, + d *Uint, +) *Uint { + // uint256 pow2 = d & -d; + // d + _negD := new(Uint).Neg(d) + pow2 := new(Uint).And(d, _negD) + d = new(Uint).Div(d, pow2) + l = new(Uint).Div(l, pow2) + + _negPow2 := new(Uint).Neg(pow2) + + value1 := new(Uint).Div(_negPow2, pow2) // (-pow2) / pow2 + value2 := new(Uint).Add(value1, One()) // (-pow2) / pow2 + 1) + value3 := new(Uint).Mul(h, value2) // h * ((-pow2) / pow2 + 1); + l = new(Uint).Add(l, value3) + + r := One() + for i := 0; i < 8; i++ { + value1 := new(Uint).Mul(d, r) // d * r + value2 := new(Uint).Sub(NewUint(2), value1) // 2 - ( d * r ) + r = new(Uint).Mul(r, value2) // r *= 2 - d * r; + } + res := new(Uint).Mul(l, r) + return res +} + +func MulDiv( + x *Uint, + y *Uint, + d *Uint, +) *Uint { + l, h := fullMul(x, y) + mm := new(Uint).MulMod(x, y, d) + + if mm.Gt(l) { + h = new(Uint).Sub(h, One()) + } + l = new(Uint).Sub(l, mm) + + if h.IsZero() { + return new(Uint).Div(l, d) + } + + if !(h.Lt(d)) { + panic("FULLDIV_OVERFLOW") + } + + return fullDiv(l, h, d) +} + +func DivRoundingUp( + x *Uint, + y *Uint, +) *Uint { + div := new(Uint).Div(x, y) + + mod := new(Uint).Mod(x, y) + return new(Uint).Add(div, gt(mod, Zero())) +} + +// HELPERs +func lt(x, y *Uint) *Uint { + if x.Lt(y) { + return One() + } else { + return Zero() + } +} + +func gt(x, y *Uint) *Uint { + if x.Gt(y) { + return One() + } else { + return Zero() + } +} diff --git a/examples/gno.land/p/demo/uint256/uint256_test.gno b/examples/gno.land/p/demo/uint256/uint256_test.gno new file mode 100644 index 00000000000..5c56abd51eb --- /dev/null +++ b/examples/gno.land/p/demo/uint256/uint256_test.gno @@ -0,0 +1,18 @@ +package uint256 + +import ( + "testing" +) + +func TestFuncs(t *testing.T) { + x := NewUint(0) + y := NewUint(1) + + // println(x.Dec()) + + var m256 bigint = 115792089237316195423570985008687907853269984665640564039457584007913129639935 + + z := FromBigint(m256) + + println(z.Dec()) +} From e84a2d639c8f6b7021330b225fd61ad23e2894ef Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Fri, 15 Mar 2024 18:47:15 +0900 Subject: [PATCH 02/23] remove int256. make separate PR later --- .../gno.land/p/demo/uint256/int256/gno.mod | 3 - .../gno.land/p/demo/uint256/int256/int256.gno | 557 ------------------ .../p/demo/uint256/int256/int256_test.gno | 1 - 3 files changed, 561 deletions(-) delete mode 100644 examples/gno.land/p/demo/uint256/int256/gno.mod delete mode 100644 examples/gno.land/p/demo/uint256/int256/int256.gno delete mode 100644 examples/gno.land/p/demo/uint256/int256/int256_test.gno diff --git a/examples/gno.land/p/demo/uint256/int256/gno.mod b/examples/gno.land/p/demo/uint256/int256/gno.mod deleted file mode 100644 index ef906c83c93..00000000000 --- a/examples/gno.land/p/demo/uint256/int256/gno.mod +++ /dev/null @@ -1,3 +0,0 @@ -module gno.land/p/demo/int256 - -require gno.land/p/demo/uint256 v0.0.0-latest diff --git a/examples/gno.land/p/demo/uint256/int256/int256.gno b/examples/gno.land/p/demo/uint256/int256/int256.gno deleted file mode 100644 index 30412935184..00000000000 --- a/examples/gno.land/p/demo/uint256/int256/int256.gno +++ /dev/null @@ -1,557 +0,0 @@ -// github.com/mempooler/int256 - -package int256 - -import ( - "gno.land/p/demo/uint256" -) - -var one = uint256.NewUint(1) - -type Int struct { - abs *uint256.Uint - neg bool -} - -func Zero() *Int { - return NewInt(0) -} - -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 { - if len(z.abs) == 0 { - return 0 - } - if z.neg { - return -1 - } - return 1 -} - -func New() *Int { - return &Int{ - abs: new(uint256.Uint), - } -} - -// SetInt64 sets z to x and returns z. -func (z *Int) SetInt64(x int64) *Int { - 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 { - if z.abs == nil { - panic("abs is nil") - } - z.abs = z.abs.SetUint64(x) - z.neg = false - return z -} - -// NewInt allocates and returns a new Int set to x. -func NewInt(x int64) *Int { - return New().SetInt64(x) -} - -func FromDecimal(s string) (*Int, error) { - return new(Int).SetString(s) -} - -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) { - origin := s - 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 -} - -// // setFromScanner implements SetString given an io.ByteScanner. -// // For documentation see comments of SetString. -// func (z *Int) setFromScanner(r io.ByteScanner, base int) (*Int, bool) { -// if _, _, err := z.scan(r, base); err != nil { -// return nil, false -// } -// // entire content must have been consumed -// if _, err := r.ReadByte(); err != io.EOF { -// return nil, false -// } -// return z, true // err == io.EOF => scan consumed all content of r -// } - -func (z *Int) Add(x, y *Int) *Int { - 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 -} - -// Sub sets z to the difference x-y and returns z. -func (z *Int) Sub(x, y *Int) *Int { - 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 -} - -// Mul sets z to the product x*y and returns z. -func (z *Int) Mul(x, y *Int) *Int { - z.abs = z.abs.Mul(x.abs, y.abs) - z.neg = x.neg != y.neg // 0 has no sign - return z -} - -// Rsh sets z = x >> n and returns z. -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(0)) - t = t.Rsh(t, n) - - _tmp := t.Add(t, NewInt(1)) - z.abs = _tmp.Abs() - z.neg = true - - 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. -// Quo implements truncated division (like Go); see QuoRem for more details. -func (z *Int) Quo(x, y *Int) *Int { - z.abs = z.abs.Div(x.abs, y.abs) - z.neg = len(z.abs) > 0 && 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. -// Rem implements truncated modulus (like Go); see QuoRem for more details. -func (z *Int) Rem(x, y *Int) *Int { - z.abs.Mod(x.abs, y.abs) - z.neg = len(z.abs) > 0 && x.neg // 0 has no sign - return z -} - -// 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 -} - -func (z *Int) Div(x, y *Int) *Int { - z.abs.Div(x.abs, y.abs) - if x.neg == y.neg { - z.neg = false - } else { - 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 -} - -// 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 -} - -// 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 - - // // TODO: implement - // big := new(big.Int).Or(x.ToBig(), y.ToBig()) - // z = MustFromBig(big) - return z -} - -// 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 := New() - // z := &Int{} - z.SetUint256(x) - return z -} - -// 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) -} - -// 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 -} - -// Clone creates a new Int identical to z -func (z *Int) Clone() *Int { - return &Int{z.abs.Clone(), z.neg} -} - -// 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 -} - -// Eq returns true if z == x -func (z *Int) Eq(x *Int) bool { - return (z.neg == x.neg) && z.abs.Eq(x.abs) -} - -// 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) - } - } -} - -// 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 -} - -// 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 -} - -// 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 -} - -// 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 -} - -// 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()) -} - -// 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 -} - -// NIL TO ZERO -func (z *Int) NilToZero() *Int { - if z == nil { - return NewInt(0) - } - return z -} - -func (z *Int) ToString() string { - if z == nil { - panic("int256: nil pointer") - } - - t := z.abs.Dec() - if z.neg { - return "-" + t - } - return t -} diff --git a/examples/gno.land/p/demo/uint256/int256/int256_test.gno b/examples/gno.land/p/demo/uint256/int256/int256_test.gno deleted file mode 100644 index e5b8059855c..00000000000 --- a/examples/gno.land/p/demo/uint256/int256/int256_test.gno +++ /dev/null @@ -1 +0,0 @@ -package int256 From b160b86f9c53312ef08fdd1823e8f00dbc802d79 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Fri, 15 Mar 2024 18:52:15 +0900 Subject: [PATCH 03/23] remove gnoswap specific methods --- .../uint256/uint256_overflow_calculation.gno | 99 ------------------- 1 file changed, 99 deletions(-) delete mode 100644 examples/gno.land/p/demo/uint256/uint256_overflow_calculation.gno diff --git a/examples/gno.land/p/demo/uint256/uint256_overflow_calculation.gno b/examples/gno.land/p/demo/uint256/uint256_overflow_calculation.gno deleted file mode 100644 index b4a9efebee5..00000000000 --- a/examples/gno.land/p/demo/uint256/uint256_overflow_calculation.gno +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: CC-BY-4.0 -// REF: https://github.com/Uniswap/solidity-lib/blob/master/contracts/libraries/FullMath.sol -package uint256 - -func fullMul( - x *Uint, - y *Uint, -) (*Uint, *Uint) { // l, h - mm := new(Uint).MulMod(x, y, MustFromDecimal(MAX_UINT256)) - - l := new(Uint).Mul(x, y) - h := new(Uint).Sub(mm, l) - - if mm.Lt(l) { - h = new(Uint).Sub(h, One()) - } - - return l, h -} - -func fullDiv( - l *Uint, - h *Uint, - d *Uint, -) *Uint { - // uint256 pow2 = d & -d; - // d - _negD := new(Uint).Neg(d) - pow2 := new(Uint).And(d, _negD) - d = new(Uint).Div(d, pow2) - l = new(Uint).Div(l, pow2) - - _negPow2 := new(Uint).Neg(pow2) - - value1 := new(Uint).Div(_negPow2, pow2) // (-pow2) / pow2 - value2 := new(Uint).Add(value1, One()) // (-pow2) / pow2 + 1) - value3 := new(Uint).Mul(h, value2) // h * ((-pow2) / pow2 + 1); - l = new(Uint).Add(l, value3) - - r := One() - for i := 0; i < 8; i++ { - value1 := new(Uint).Mul(d, r) // d * r - value2 := new(Uint).Sub(NewUint(2), value1) // 2 - ( d * r ) - r = new(Uint).Mul(r, value2) // r *= 2 - d * r; - } - res := new(Uint).Mul(l, r) - return res -} - -func MulDiv( - x *Uint, - y *Uint, - d *Uint, -) *Uint { - l, h := fullMul(x, y) - mm := new(Uint).MulMod(x, y, d) - - if mm.Gt(l) { - h = new(Uint).Sub(h, One()) - } - l = new(Uint).Sub(l, mm) - - if h.IsZero() { - return new(Uint).Div(l, d) - } - - if !(h.Lt(d)) { - panic("FULLDIV_OVERFLOW") - } - - return fullDiv(l, h, d) -} - -func DivRoundingUp( - x *Uint, - y *Uint, -) *Uint { - div := new(Uint).Div(x, y) - - mod := new(Uint).Mod(x, y) - return new(Uint).Add(div, gt(mod, Zero())) -} - -// HELPERs -func lt(x, y *Uint) *Uint { - if x.Lt(y) { - return One() - } else { - return Zero() - } -} - -func gt(x, y *Uint) *Uint { - if x.Gt(y) { - return One() - } else { - return Zero() - } -} From 8eda43cca5fff03ba02bce27f502d1640cbfc8bf Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Fri, 15 Mar 2024 18:53:28 +0900 Subject: [PATCH 04/23] update license --- examples/gno.land/p/demo/uint256/LICENSE | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 examples/gno.land/p/demo/uint256/LICENSE diff --git a/examples/gno.land/p/demo/uint256/LICENSE b/examples/gno.land/p/demo/uint256/LICENSE new file mode 100644 index 00000000000..505e4324f88 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright 2020 uint256 Authors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From b502d0743501031b972eef3dfb29ee686e6a04f8 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Mon, 18 Mar 2024 18:27:18 +0900 Subject: [PATCH 05/23] basic test setup and add some functions --- examples/gno.land/p/demo/int256/gno.mod | 3 + examples/gno.land/p/demo/int256/int256.gno | 557 ++++++++++++++++++ .../gno.land/p/demo/int256/int256_test.gno | 1 + .../gno.land/p/demo/uint256/bits_errors.gno | 11 - examples/gno.land/p/demo/uint256/consts.gno | 5 - .../gno.land/p/demo/uint256/conversion.gno | 293 +++++++++ examples/gno.land/p/demo/uint256/uint256.gno | 161 ++++- .../gno.land/p/demo/uint256/uint256_test.gno | 367 +++++++++++- 8 files changed, 1370 insertions(+), 28 deletions(-) create mode 100644 examples/gno.land/p/demo/int256/gno.mod create mode 100644 examples/gno.land/p/demo/int256/int256.gno create mode 100644 examples/gno.land/p/demo/int256/int256_test.gno delete mode 100644 examples/gno.land/p/demo/uint256/bits_errors.gno delete mode 100644 examples/gno.land/p/demo/uint256/consts.gno create mode 100644 examples/gno.land/p/demo/uint256/conversion.gno 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..30412935184 --- /dev/null +++ b/examples/gno.land/p/demo/int256/int256.gno @@ -0,0 +1,557 @@ +// github.com/mempooler/int256 + +package int256 + +import ( + "gno.land/p/demo/uint256" +) + +var one = uint256.NewUint(1) + +type Int struct { + abs *uint256.Uint + neg bool +} + +func Zero() *Int { + return NewInt(0) +} + +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 { + if len(z.abs) == 0 { + return 0 + } + if z.neg { + return -1 + } + return 1 +} + +func New() *Int { + return &Int{ + abs: new(uint256.Uint), + } +} + +// SetInt64 sets z to x and returns z. +func (z *Int) SetInt64(x int64) *Int { + 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 { + if z.abs == nil { + panic("abs is nil") + } + z.abs = z.abs.SetUint64(x) + z.neg = false + return z +} + +// NewInt allocates and returns a new Int set to x. +func NewInt(x int64) *Int { + return New().SetInt64(x) +} + +func FromDecimal(s string) (*Int, error) { + return new(Int).SetString(s) +} + +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) { + origin := s + 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 +} + +// // setFromScanner implements SetString given an io.ByteScanner. +// // For documentation see comments of SetString. +// func (z *Int) setFromScanner(r io.ByteScanner, base int) (*Int, bool) { +// if _, _, err := z.scan(r, base); err != nil { +// return nil, false +// } +// // entire content must have been consumed +// if _, err := r.ReadByte(); err != io.EOF { +// return nil, false +// } +// return z, true // err == io.EOF => scan consumed all content of r +// } + +func (z *Int) Add(x, y *Int) *Int { + 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 +} + +// Sub sets z to the difference x-y and returns z. +func (z *Int) Sub(x, y *Int) *Int { + 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 +} + +// Mul sets z to the product x*y and returns z. +func (z *Int) Mul(x, y *Int) *Int { + z.abs = z.abs.Mul(x.abs, y.abs) + z.neg = x.neg != y.neg // 0 has no sign + return z +} + +// Rsh sets z = x >> n and returns z. +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(0)) + t = t.Rsh(t, n) + + _tmp := t.Add(t, NewInt(1)) + z.abs = _tmp.Abs() + z.neg = true + + 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. +// Quo implements truncated division (like Go); see QuoRem for more details. +func (z *Int) Quo(x, y *Int) *Int { + z.abs = z.abs.Div(x.abs, y.abs) + z.neg = len(z.abs) > 0 && 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. +// Rem implements truncated modulus (like Go); see QuoRem for more details. +func (z *Int) Rem(x, y *Int) *Int { + z.abs.Mod(x.abs, y.abs) + z.neg = len(z.abs) > 0 && x.neg // 0 has no sign + return z +} + +// 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 +} + +func (z *Int) Div(x, y *Int) *Int { + z.abs.Div(x.abs, y.abs) + if x.neg == y.neg { + z.neg = false + } else { + 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 +} + +// 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 +} + +// 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 + + // // TODO: implement + // big := new(big.Int).Or(x.ToBig(), y.ToBig()) + // z = MustFromBig(big) + return z +} + +// 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 := New() + // z := &Int{} + z.SetUint256(x) + return z +} + +// 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) +} + +// 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 +} + +// Clone creates a new Int identical to z +func (z *Int) Clone() *Int { + return &Int{z.abs.Clone(), z.neg} +} + +// 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 +} + +// Eq returns true if z == x +func (z *Int) Eq(x *Int) bool { + return (z.neg == x.neg) && z.abs.Eq(x.abs) +} + +// 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) + } + } +} + +// 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 +} + +// 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 +} + +// 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 +} + +// 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 +} + +// 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()) +} + +// 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 +} + +// NIL TO ZERO +func (z *Int) NilToZero() *Int { + if z == nil { + return NewInt(0) + } + return z +} + +func (z *Int) ToString() string { + if z == nil { + panic("int256: nil pointer") + } + + t := z.abs.Dec() + if z.neg { + return "-" + t + } + return t +} 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..e5b8059855c --- /dev/null +++ b/examples/gno.land/p/demo/int256/int256_test.gno @@ -0,0 +1 @@ +package int256 diff --git a/examples/gno.land/p/demo/uint256/bits_errors.gno b/examples/gno.land/p/demo/uint256/bits_errors.gno deleted file mode 100644 index 5de53099802..00000000000 --- a/examples/gno.land/p/demo/uint256/bits_errors.gno +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -package uint256 - -import "errors" - -var ( - overflowError = errors.New("u256: integer overflow") - divideError = errors.New("u256: integer divide by zero") -) \ No newline at end of file diff --git a/examples/gno.land/p/demo/uint256/consts.gno b/examples/gno.land/p/demo/uint256/consts.gno deleted file mode 100644 index 2de43d58262..00000000000 --- a/examples/gno.land/p/demo/uint256/consts.gno +++ /dev/null @@ -1,5 +0,0 @@ -package uint256 - -const ( - MAX_UINT256 = "115792089237316195423570985008687907853269984665640564039457584007913129639935" -) diff --git a/examples/gno.land/p/demo/uint256/conversion.gno b/examples/gno.land/p/demo/uint256/conversion.gno new file mode 100644 index 00000000000..2262b1733db --- /dev/null +++ b/examples/gno.land/p/demo/uint256/conversion.gno @@ -0,0 +1,293 @@ +package uint256 + +import ( + "encoding/binary" +) + +func (z *Uint) SetBytes1(in []byte) *Uint { + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = uint64(in[0]) + return z +} + +func (z *Uint) SetBytes2(in []byte) *Uint { + _ = in[1] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = uint64(binary.BigEndian.Uint16(in[0:2])) + return z +} + +func (z *Uint) SetBytes3(in []byte) *Uint { + _ = in[2] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])<<16 + return z +} + +func (z *Uint) SetBytes4(in []byte) *Uint { + _ = in[3] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = uint64(binary.BigEndian.Uint32(in[0:4])) + return z +} + +func (z *Uint) SetBytes5(in []byte) *Uint { + _ = in[4] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = bigEndianUint40(in[0:5]) + return z +} + +func (z *Uint) SetBytes6(in []byte) *Uint { + _ = in[5] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = bigEndianUint48(in[0:6]) + return z +} + +func (z *Uint) SetBytes7(in []byte) *Uint { + _ = in[6] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = bigEndianUint56(in[0:7]) + return z +} + +// SetBytes8 is identical to SetBytes(in[:8]), but panics is input is too short +func (z *Uint) SetBytes8(in []byte) *Uint { + _ = in[7] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + z.arr[0] = binary.BigEndian.Uint64(in[0:8]) + return z +} + +func (z *Uint) SetBytes9(in []byte) *Uint { + _ = in[8] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = uint64(in[0]) + z.arr[0] = binary.BigEndian.Uint64(in[1:9]) + return z +} + +func (z *Uint) SetBytes10(in []byte) *Uint { + _ = in[9] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = uint64(binary.BigEndian.Uint16(in[0:2])) + z.arr[0] = binary.BigEndian.Uint64(in[2:10]) + return z +} + +func (z *Uint) SetBytes11(in []byte) *Uint { + _ = in[10] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])<<16 + z.arr[0] = binary.BigEndian.Uint64(in[3:11]) + return z +} + + +func (z *Uint) SetBytes12(in []byte) *Uint { + _ = in[11] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = uint64(binary.BigEndian.Uint32(in[0:4])) + z.arr[0] = binary.BigEndian.Uint64(in[4:12]) + return z +} + +func (z *Uint) SetBytes13(in []byte) *Uint { + _ = in[12] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = bigEndianUint40(in[0:5]) + z.arr[0] = binary.BigEndian.Uint64(in[5:13]) + return z +} + +func (z *Uint) SetBytes14(in []byte) *Uint { + _ = in[13] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = bigEndianUint48(in[0:6]) + z.arr[0] = binary.BigEndian.Uint64(in[6:14]) + return z +} + +func (z *Uint) SetBytes15(in []byte) *Uint { + _ = in[14] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = bigEndianUint56(in[0:7]) + z.arr[0] = binary.BigEndian.Uint64(in[7:15]) + return z +} + +// SetBytes16 is identical to SetBytes(in[:16]), but panics is input is too short +func (z *Uint) SetBytes16(in []byte) *Uint { + _ = in[15] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3], z.arr[2] = 0, 0 + z.arr[1] = binary.BigEndian.Uint64(in[0:8]) + z.arr[0] = binary.BigEndian.Uint64(in[8:16]) + return z +} + +func (z *Uint) SetBytes17(in []byte) *Uint { + _ = in[16] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = uint64(in[0]) + z.arr[1] = binary.BigEndian.Uint64(in[1:9]) + z.arr[0] = binary.BigEndian.Uint64(in[9:17]) + return z +} + +func (z *Uint) SetBytes18(in []byte) *Uint { + _ = in[17] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = uint64(binary.BigEndian.Uint16(in[0:2])) + z.arr[1] = binary.BigEndian.Uint64(in[2:10]) + z.arr[0] = binary.BigEndian.Uint64(in[10:18]) + return z +} + +func (z *Uint) SetBytes19(in []byte) *Uint { + _ = in[18] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])<<16 + z.arr[1] = binary.BigEndian.Uint64(in[3:11]) + z.arr[0] = binary.BigEndian.Uint64(in[11:19]) + return z +} + +func (z *Uint) SetBytes20(in []byte) *Uint { + _ = in[19] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = uint64(binary.BigEndian.Uint32(in[0:4])) + z.arr[1] = binary.BigEndian.Uint64(in[4:12]) + z.arr[0] = binary.BigEndian.Uint64(in[12:20]) + return z +} + +func (z *Uint) SetBytes21(in []byte) *Uint { + _ = in[20] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = bigEndianUint40(in[0:5]) + z.arr[1] = binary.BigEndian.Uint64(in[5:13]) + z.arr[0] = binary.BigEndian.Uint64(in[13:21]) + return z +} + +func (z *Uint) SetBytes22(in []byte) *Uint { + _ = in[21] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = bigEndianUint48(in[0:6]) + z.arr[1] = binary.BigEndian.Uint64(in[6:14]) + z.arr[0] = binary.BigEndian.Uint64(in[14:22]) + return z +} + +func (z *Uint) SetBytes23(in []byte) *Uint { + _ = in[22] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = bigEndianUint56(in[0:7]) + z.arr[1] = binary.BigEndian.Uint64(in[7:15]) + z.arr[0] = binary.BigEndian.Uint64(in[15:23]) + return z +} + +// SetBytes24 is identical to SetBytes(in[:24]), but panics is input is too short +func (z *Uint) SetBytes24(in []byte) *Uint { + _ = in[23] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = 0 + z.arr[2] = binary.BigEndian.Uint64(in[0:8]) + z.arr[1] = binary.BigEndian.Uint64(in[8:16]) + z.arr[0] = binary.BigEndian.Uint64(in[16:24]) + return z +} + +func (z *Uint) SetBytes25(in []byte) *Uint { + _ = in[24] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = uint64(in[0]) + z.arr[2] = binary.BigEndian.Uint64(in[1:9]) + z.arr[1] = binary.BigEndian.Uint64(in[9:17]) + z.arr[0] = binary.BigEndian.Uint64(in[17:25]) + return z +} + +func (z *Uint) SetBytes26(in []byte) *Uint { + _ = in[25] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = uint64(binary.BigEndian.Uint16(in[0:2])) + z.arr[2] = binary.BigEndian.Uint64(in[2:10]) + z.arr[1] = binary.BigEndian.Uint64(in[10:18]) + z.arr[0] = binary.BigEndian.Uint64(in[18:26]) + return z +} + +func (z *Uint) SetBytes27(in []byte) *Uint { + _ = in[26] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])<<16 + z.arr[2] = binary.BigEndian.Uint64(in[3:11]) + z.arr[1] = binary.BigEndian.Uint64(in[11:19]) + z.arr[0] = binary.BigEndian.Uint64(in[19:27]) + return z +} + +func (z *Uint) SetBytes28(in []byte) *Uint { + _ = in[27] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = uint64(binary.BigEndian.Uint32(in[0:4])) + z.arr[2] = binary.BigEndian.Uint64(in[4:12]) + z.arr[1] = binary.BigEndian.Uint64(in[12:20]) + z.arr[0] = binary.BigEndian.Uint64(in[20:28]) + return z +} + +func (z *Uint) SetBytes29(in []byte) *Uint { + _ = in[23] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = bigEndianUint40(in[0:5]) + z.arr[2] = binary.BigEndian.Uint64(in[5:13]) + z.arr[1] = binary.BigEndian.Uint64(in[13:21]) + z.arr[0] = binary.BigEndian.Uint64(in[21:29]) + return z +} + + +func (z *Uint) SetBytes30(in []byte) *Uint { + _ = in[29] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = bigEndianUint48(in[0:6]) + z.arr[2] = binary.BigEndian.Uint64(in[6:14]) + z.arr[1] = binary.BigEndian.Uint64(in[14:22]) + z.arr[0] = binary.BigEndian.Uint64(in[22:30]) + return z +} + +func (z *Uint) SetBytes31(in []byte) *Uint { + _ = in[30] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = bigEndianUint56(in[0:7]) + z.arr[2] = binary.BigEndian.Uint64(in[7:15]) + z.arr[1] = binary.BigEndian.Uint64(in[15:23]) + z.arr[0] = binary.BigEndian.Uint64(in[23:31]) + return z +} + +func (z *Uint) SetBytes32(in []byte) *Uint { + _ = in[31] // bounds check hint to compiler; see golang.org/issue/14808 + z.arr[3] = binary.BigEndian.Uint64(in[0:8]) + z.arr[2] = binary.BigEndian.Uint64(in[8:16]) + z.arr[1] = binary.BigEndian.Uint64(in[16:24]) + z.arr[0] = binary.BigEndian.Uint64(in[24:32]) + return z +} + +// Utility methods that are "missing" among the bigEndian.UintXX methods. + +func bigEndianUint40(b []byte) uint64 { + _ = b[4] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[4]) | uint64(b[3])<<8 | uint64(b[2])<<16 | uint64(b[1])<<24 | + uint64(b[0])<<32 +} + +func bigEndianUint56(b []byte) uint64 { + _ = b[6] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[6]) | uint64(b[5])<<8 | uint64(b[4])<<16 | uint64(b[3])<<24 | + uint64(b[2])<<32 | uint64(b[1])<<40 | uint64(b[0])<<48 +} + +func bigEndianUint48(b []byte) uint64 { + _ = b[5] // bounds check hint to compiler; see golang.org/issue/14808 + return uint64(b[5]) | uint64(b[4])<<8 | uint64(b[3])<<16 | uint64(b[2])<<24 | + uint64(b[1])<<32 | uint64(b[0])<<40 +} \ No newline at end of file diff --git a/examples/gno.land/p/demo/uint256/uint256.gno b/examples/gno.land/p/demo/uint256/uint256.gno index 0f8b346d526..088100eca36 100644 --- a/examples/gno.land/p/demo/uint256/uint256.gno +++ b/examples/gno.land/p/demo/uint256/uint256.gno @@ -63,6 +63,16 @@ func (z *Uint) SetUint64(x uint64) *Uint { return z } +// LtUint64 returns true if z is smaller than n +func (z *Uint) LtUint64(n uint64) bool { + return z.arr[0] < n && (z.arr[1]|z.arr[2]|z.arr[3]) == 0 +} + +// GtUint64 returns true if z is larger than n +func (z *Uint) GtUint64(n uint64) bool { + return z.arr[0] > n || (z.arr[1]|z.arr[2]|z.arr[3]) != 0 +} + // IsUint64 reports whether z can be represented as a uint64. func (z *Uint) IsUint64() bool { return (z.arr[1] | z.arr[2] | z.arr[3]) == 0 @@ -73,29 +83,63 @@ func (z *Uint) IsZero() bool { return (z.arr[0] | z.arr[1] | z.arr[2] | z.arr[3]) == 0 } +// Sign returns: +// +// -1 if z < 0 +// 0 if z == 0 +// +1 if z > 0 +// +// Where z is interpreted as a two's complement signed number +func (z *Uint) Sign() int { + if z.IsZero() { + return 0 + } + if z.arr[3] < 0x8000000000000000 { + return 1 + } + return -1 +} + + // Clear sets z to 0 func (z *Uint) Clear() *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 0 return z } -// Neg returns -x mod 2**256. -func (z *Uint) Neg(x *Uint) *Uint { - return z.Sub(new(Uint), x) -} - // SetAllOne sets all the bits of z to 1 func (z *Uint) SetAllOne() *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, MaxUint64 return z } +// Neg returns -x mod 2**256. +func (z *Uint) Neg(x *Uint) *Uint { + return z.Sub(new(Uint), x) +} + // Not sets z = ^x and returns z. func (z *Uint) Not(x *Uint) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = ^x.arr[3], ^x.arr[2], ^x.arr[1], ^x.arr[0] return z } +// Sgt interprets z and x as signed integers, and returns +// true if z > x +func (z *Uint) Sgt(x *Uint) bool { + zSign := z.Sign() + xSign := x.Sign() + + switch { + case zSign >= 0 && xSign < 0: + return true + case zSign < 0 && xSign >= 0: + return false + default: + return z.Gt(x) + } +} + // Lt returns true if z < x func (z *Uint) Lt(x *Uint) bool { // z < x <=> z - x < 0 i.e. when subtraction overflows. @@ -766,6 +810,25 @@ func (z *Uint) Xor(x, y *Uint) *Uint { return z } +// Byte sets z to the value of the byte at position n, +// with 'z' considered as a big-endian 32-byte integer +// if 'n' > 32, f is set to 0 +// Example: f = '5', n=31 => 5 +func (z *Uint) Byte(n *Uint) *Uint { + // in z, z.arr[0] is the least significant + if number, overflow := n.Uint64WithOverflow(); !overflow { + if number < 32 { + number := z.arr[4-1-number/8] + offset := (n.arr[0] & 0x7) << 3 // 8*(n.d % 8) + z.arr[0] = (number & (0xff00000000000000 >> offset)) >> (56 - offset) + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + return z + } + } + + return z.Clear() +} + // MarshalJSON implements json.Marshaler. // MarshalJSON marshals using the 'decimal string' representation. This is _not_ compatible // with big.Uint: big.Uint marshals into JSON 'native' numeric format. @@ -1232,6 +1295,18 @@ func (z *Uint) Mod(x, y *Uint) *Uint { return z } +// DivMod sets z to the quotient x div y and m to the modulus x mod y and returns the pair (z, m) for y != 0. +// If y == 0, both z and m are set to 0 (OBS: differs from the big.Int) +func (z *Uint) DivMod(x, y, m *Uint) (*Uint, *Uint) { + if y.IsZero() { + return z.Clear(), m.Clear() + } + var quot Uint + *m = udivrem(quot.arr[:], x.arr[:], y) + *z = quot + return z, m +} + // Clone creates a new Int identical to z func (z *Uint) Clone() *Uint { var x Uint @@ -1262,3 +1337,79 @@ func (z *Uint) AndNot(x, y *Uint) *Uint { z.arr[3] = x.arr[3] &^ y.arr[3] return z } + + +// SetBytes interprets buf as the bytes of a big-endian unsigned +// integer, sets z to that value, and returns z. +// If buf is larger than 32 bytes, the last 32 bytes is used. +func (z *Uint) SetBytes(buf []byte) *Uint { + switch l := len(buf); l { + case 0: + z.Clear() + case 1: + z.SetBytes1(buf) + case 2: + z.SetBytes2(buf) + case 3: + z.SetBytes3(buf) + case 4: + z.SetBytes4(buf) + case 5: + z.SetBytes5(buf) + case 6: + z.SetBytes6(buf) + case 7: + z.SetBytes7(buf) + case 8: + z.SetBytes8(buf) + case 9: + z.SetBytes9(buf) + case 10: + z.SetBytes10(buf) + case 11: + z.SetBytes11(buf) + case 12: + z.SetBytes12(buf) + case 13: + z.SetBytes13(buf) + case 14: + z.SetBytes14(buf) + case 15: + z.SetBytes15(buf) + case 16: + z.SetBytes16(buf) + case 17: + z.SetBytes17(buf) + case 18: + z.SetBytes18(buf) + case 19: + z.SetBytes19(buf) + case 20: + z.SetBytes20(buf) + case 21: + z.SetBytes21(buf) + case 22: + z.SetBytes22(buf) + case 23: + z.SetBytes23(buf) + case 24: + z.SetBytes24(buf) + case 25: + z.SetBytes25(buf) + case 26: + z.SetBytes26(buf) + case 27: + z.SetBytes27(buf) + case 28: + z.SetBytes28(buf) + case 29: + z.SetBytes29(buf) + case 30: + z.SetBytes30(buf) + case 31: + z.SetBytes31(buf) + default: + z.SetBytes32(buf[l-32:]) + } + return z +} diff --git a/examples/gno.land/p/demo/uint256/uint256_test.gno b/examples/gno.land/p/demo/uint256/uint256_test.gno index 5c56abd51eb..3f56014be69 100644 --- a/examples/gno.land/p/demo/uint256/uint256_test.gno +++ b/examples/gno.land/p/demo/uint256/uint256_test.gno @@ -1,18 +1,371 @@ package uint256 import ( + "bytes" + "encoding/hex" + "strings" "testing" + + "gno.land/p/demo/ufmt" + i256 "gno.land/p/demo/int256" ) -func TestFuncs(t *testing.T) { - x := NewUint(0) - y := NewUint(1) +// TODO: Set up simple tests for now and modify them later to check for more complex cases. + +func parseUintFromString(s string) (*Uint, error) { + if s == "0" { + return NewUint(0), nil + } + value, err := parseUint(s, 16, 64) + if err != nil { + return nil, err + } + return NewUint(value), nil +} + +type binOpTest struct { + x, y, want string +} + +// TODO: use `SetBytes` to test hex strings +func TestAdd(t *testing.T) { + tests := []binOpTest{ + {"0", "1", "1"}, + {"1", "0", "1"}, + {"1", "1", "2"}, + {"1", "3", "4"}, + {"10", "10", "20"}, + } + + for _, tc := range tests { + x, err := parseUintFromString(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := parseUintFromString(tc.y) + if err != nil { + t.Error(err) + continue + } - // println(x.Dec()) + want, err := parseUintFromString(tc.want) + if err != nil { + t.Error(err) + continue + } - var m256 bigint = 115792089237316195423570985008687907853269984665640564039457584007913129639935 + got := &Uint{} + got.Add(x, y) - z := FromBigint(m256) + for i := range got.arr { + if got.arr[i] != want.arr[i] { + t.Errorf("Add(%s, %s) = %v, want %v", tc.x, tc.y, got, want) + break + } + } + } +} + +// TODO: need to test negative results +func TestSub(t *testing.T) { + tests := []binOpTest{ + {"1", "0", "1"}, + {"1", "1", "0"}, + {"10", "10", "0"}, + } + + for _, tc := range tests { + x, err := parseUintFromString(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := parseUintFromString(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := parseUintFromString(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Sub(x, y) + + for i := range got.arr { + if got.arr[i] != want.arr[i] { + t.Errorf("Sub(%s, %s) = %v, want %v", tc.x, tc.y, got, want) + break + } + } + } +} + +func TestMul(t *testing.T) { + tests := []binOpTest{ + {"1", "0", "0"}, + {"1", "1", "1"}, + {"10", "10", "100"}, + } + + for _, tc := range tests { + x, err := parseUintFromString(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := parseUintFromString(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := parseUintFromString(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Mul(x, y) + + for i := range got.arr { + if got.arr[i] != want.arr[i] { + t.Errorf("Mul(%s, %s) = %v, want %v", tc.x, tc.y, got, want) + break + } + } + } +} + +func TestDivMod(t *testing.T) { + tests := []struct { + x string + y string + wantDiv string + wantMod string + }{ + {"1", "1", "1", "0"}, + {"10", "10", "1", "0"}, + {"100", "10", "10", "0"}, + } + + for _, tc := range tests { + x, err := parseUintFromString(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := parseUintFromString(tc.y) + if err != nil { + t.Error(err) + continue + } + + wantDiv, err := parseUintFromString(tc.wantDiv) + if err != nil { + t.Error(err) + continue + } + + wantMod, err := parseUintFromString(tc.wantMod) + if err != nil { + t.Error(err) + continue + } + + gotDiv := new(Uint) + gotMod := new(Uint) + gotDiv.DivMod(x, y, gotMod) + + for i := range gotDiv.arr { + if gotDiv.arr[i] != wantDiv.arr[i] { + t.Errorf("DivMod(%s, %s) got Div %v, want Div %v", tc.x, tc.y, gotDiv, wantDiv) + break + } + } + for i := range gotMod.arr { + if gotMod.arr[i] != wantMod.arr[i] { + t.Errorf("DivMod(%s, %s) got Mod %v, want Mod %v", tc.x, tc.y, gotMod, wantMod) + break + } + } + } +} + +func TestIsZero(t *testing.T) { + tests := []struct { + x string + want bool + }{ + {"0", true}, + {"1", false}, + {"10", false}, + } + + for _, tc := range tests { + x, err := parseUintFromString(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 TestIsUint64(t *testing.T) { + tests := []struct { + x string + want bool + }{ + {"0", true}, + {"1", true}, + {"10", true}, + {"ffffffffffffffff", true}, + {"10000000000000000", true}, + } + + for _, tc := range tests { + x := new(Uint).SetBytes(hex2Bytes(tc.x)) + got := x.IsUint64() + + if got != tc.want { + t.Errorf("IsUint64(%s) = %v, want %v", tc.x, got, tc.want) + } + } +} + +func TestLtUint64(t *testing.T) { + tests := []struct { + x string + y uint64 + want bool + }{ + {"0", 1, true}, + {"1", 0, false}, + {"10", 10, false}, + {"ffffffffffffffff", 0, false}, + {"10000000000000000", 0, false}, + } + + for _, tc := range tests { + x := new(Uint).SetBytes(hex2Bytes(tc.x)) + got := x.LtUint64(tc.y) + + if got != tc.want { + t.Errorf("LtUint64(%s, %d) = %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", "1", 0}, + {"10", "10", 0}, + {"10", "11", -1}, + {"11", "10", 1}, + } + + for _, tc := range tests { + x, err := parseUintFromString(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := parseUintFromString(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 hex2Bytes(s string) []byte { + h, _ := hex.DecodeString(s) + return h +} + +func checkOverflow(f *Uint, overflow bool) error { + hb := hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + max := NewUint(0).SetBytes(hb) + shouldOverflow := f.Cmp(max) > 0 + if overflow != shouldOverflow { + return ufmt.Errorf("overflow should be %v, but got %v", overflow, shouldOverflow) + } + + return nil +} + +func TestByte(t *testing.T) { + z := new(Uint).SetBytes(hex2Bytes("ABCDEF09080706050403020100000000000000000000000000000000000000ef")) + actual := z.Byte(NewUint(0)) + expected := new(Uint).SetBytes(hex2Bytes("00000000000000000000000000000000000000000000000000000000000000ab")) + if !actual.Eq(expected) { + t.Fatalf("Expected %x, got %x", expected, actual) + } + + z = new(Uint).SetBytes(hex2Bytes("ABCDEF09080706050403020100000000000000000000000000000000000000ef")) + actual = z.Byte(NewUint(31)) + expected = new(Uint).SetBytes(hex2Bytes("00000000000000000000000000000000000000000000000000000000000000ef")) + if !actual.Eq(expected) { + t.Fatalf("Expected %x, got %x", expected, actual) + } + + z = new(Uint).SetBytes(hex2Bytes("ABCDEF09080706050403020100000000000000000000000000000000000000ef")) + actual = z.Byte(NewUint(32)) + expected =new(Uint).SetBytes(hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) + if !actual.Eq(expected) { + t.Fatalf("Expected %x, got %x", expected, actual) + } + + z = new(Uint).SetBytes(hex2Bytes("ABCDEF0908070605040302011111111111111111111111111111111111111111")) + actual = z.Byte(new(Uint).SetBytes(hex2Bytes("f000000000000000000000000000000000000000000000000000000000000001"))) + expected = new(Uint).SetBytes(hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) + if !actual.Eq(expected) { + t.Fatalf("Expected %x, got %x", expected, actual) + } +} + +func TestSGT(t *testing.T) { + x := new(Uint).SetBytes(hex2Bytes("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")) + y := new(Uint).SetBytes(hex2Bytes("00")) + actual := x.Sgt(y) + if actual { + t.Fatalf("Expected %v false", actual) + } + + x = new(Uint).SetBytes(hex2Bytes("00")) + y = new(Uint).SetBytes(hex2Bytes("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")) + actual = x.Sgt(y) + if !actual { + t.Fatalf("Expected %v true", actual) + } +} - println(z.Dec()) +type logicOpTest struct { + x, y, want string } From 38bd979c8bc688e1ae05865cac16ae83ac43419d Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Mon, 18 Mar 2024 22:33:17 +0900 Subject: [PATCH 06/23] organize functions by category --- .../gno.land/p/demo/uint256/arithmetic.gno | 406 ++++++ .../p/demo/uint256/arithmetic_test.gno | 182 +++ examples/gno.land/p/demo/uint256/bitwise.gno | 263 ++++ .../gno.land/p/demo/uint256/bitwise_test.gno | 64 + examples/gno.land/p/demo/uint256/cmp.gno | 120 ++ examples/gno.land/p/demo/uint256/cmp_test.gno | 102 ++ .../gno.land/p/demo/uint256/conversion.gno | 233 ++- .../p/demo/uint256/conversion_test.gno | 60 + examples/gno.land/p/demo/uint256/error.gno | 73 + examples/gno.land/p/demo/uint256/uint256.gno | 1290 +---------------- .../gno.land/p/demo/uint256/uint256_test.gno | 314 ---- examples/gno.land/p/demo/uint256/utils.gno | 195 +++ 12 files changed, 1753 insertions(+), 1549 deletions(-) create mode 100644 examples/gno.land/p/demo/uint256/arithmetic.gno create mode 100644 examples/gno.land/p/demo/uint256/arithmetic_test.gno create mode 100644 examples/gno.land/p/demo/uint256/bitwise.gno create mode 100644 examples/gno.land/p/demo/uint256/bitwise_test.gno create mode 100644 examples/gno.land/p/demo/uint256/cmp.gno create mode 100644 examples/gno.land/p/demo/uint256/cmp_test.gno create mode 100644 examples/gno.land/p/demo/uint256/conversion_test.gno create mode 100644 examples/gno.land/p/demo/uint256/error.gno create mode 100644 examples/gno.land/p/demo/uint256/utils.gno diff --git a/examples/gno.land/p/demo/uint256/arithmetic.gno b/examples/gno.land/p/demo/uint256/arithmetic.gno new file mode 100644 index 00000000000..2bac1ea9ca4 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/arithmetic.gno @@ -0,0 +1,406 @@ +// arithmetic provides arithmetic operations for Uint objects. +// This includes basic binary operations such as addition, subtraction, multiplication, division, and modulo operations +// as well as overflow checks, and negation. These functions are essential for numeric +// calculations using 256-bit unsigned integers. +package uint256 + +import ( + "math/bits" +) + +// Add sets z to the sum x+y +func (z *Uint) Add(x, y *Uint) *Uint { + var carry uint64 + z.arr[0], carry = bits.Add64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Add64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Add64(x.arr[2], y.arr[2], carry) + z.arr[3], _ = bits.Add64(x.arr[3], y.arr[3], carry) + return z +} + +// AddOverflow sets z to the sum x+y, and returns z and whether overflow occurred +func (z *Uint) AddOverflow(x, y *Uint) (*Uint, bool) { + var carry uint64 + z.arr[0], carry = bits.Add64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Add64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Add64(x.arr[2], y.arr[2], carry) + z.arr[3], carry = bits.Add64(x.arr[3], y.arr[3], carry) + return z, carry != 0 +} + +// Sub sets z to the difference x-y +func (z *Uint) Sub(x, y *Uint) *Uint { + var carry uint64 + z.arr[0], carry = bits.Sub64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Sub64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Sub64(x.arr[2], y.arr[2], carry) + z.arr[3], _ = bits.Sub64(x.arr[3], y.arr[3], carry) + return z +} + +// SubOverflow sets z to the difference x-y and returns z and true if the operation underflowed +func (z *Uint) SubOverflow(x, y *Uint) (*Uint, bool) { + var carry uint64 + z.arr[0], carry = bits.Sub64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = bits.Sub64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = bits.Sub64(x.arr[2], y.arr[2], carry) + z.arr[3], carry = bits.Sub64(x.arr[3], y.arr[3], carry) + return z, carry != 0 +} + +// Neg returns -x mod 2^256. +func (z *Uint) Neg(x *Uint) *Uint { + return z.Sub(new(Uint), x) +} + +// commented out for possible overflow +// Mul sets z to the product x*y +func (z *Uint) Mul(x, y *Uint) *Uint { + var ( + res Uint + carry uint64 + res1, res2, res3 uint64 + ) + + carry, res.arr[0] = bits.Mul64(x.arr[0], y.arr[0]) + carry, res1 = umulHop(carry, x.arr[1], y.arr[0]) + carry, res2 = umulHop(carry, x.arr[2], y.arr[0]) + res3 = x.arr[3]*y.arr[0] + carry + + carry, res.arr[1] = umulHop(res1, x.arr[0], y.arr[1]) + carry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry) + res3 = res3 + x.arr[2]*y.arr[1] + carry + + carry, res.arr[2] = umulHop(res2, x.arr[0], y.arr[2]) + res3 = res3 + x.arr[1]*y.arr[2] + carry + + res.arr[3] = res3 + x.arr[0]*y.arr[3] + + return z.Set(&res) +} + +// MulOverflow sets z to the product x*y, and returns z and whether overflow occurred +func (z *Uint) MulOverflow(x, y *Uint) (*Uint, bool) { + p := umul(x, y) + copy(z.arr[:], p[:4]) + return z, (p[4] | p[5] | p[6] | p[7]) != 0 +} + +// commented out for possible overflow +// Div sets z to the quotient x/y for returns z. +// If y == 0, z is set to 0 +func (z *Uint) Div(x, y *Uint) *Uint { + if y.IsZero() || y.Gt(x) { + return z.Clear() + } + if x.Eq(y) { + return z.SetOne() + } + // Shortcut some cases + if x.IsUint64() { + return z.SetUint64(x.Uint64() / y.Uint64()) + } + + // At this point, we know + // x/y ; x > y > 0 + + var quot Uint + udivrem(quot.arr[:], x.arr[:], y) + return z.Set(") +} + +// MulMod calculates the modulo-m multiplication of x and y and +// returns z. +// If m == 0, z is set to 0 (OBS: differs from the big.Int) +func (z *Uint) MulMod(x, y, m *Uint) *Uint { + if x.IsZero() || y.IsZero() || m.IsZero() { + return z.Clear() + } + p := umul(x, y) + + if m.arr[3] != 0 { + mu := Reciprocal(m) + r := reduce4(p, m, mu) + return z.Set(&r) + } + + var ( + pl Uint + ph Uint + ) + + pl = Uint{arr: [4]uint64{p[0], p[1], p[2], p[3]}} + ph = Uint{arr: [4]uint64{p[4], p[5], p[6], p[7]}} + + // If the multiplication is within 256 bits use Mod(). + if ph.IsZero() { + return z.Mod(&pl, m) + } + + var quot [8]uint64 + rem := udivrem(quot[:], p[:], m) + return z.Set(&rem) +} + +// 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.Uint) +func (z *Uint) Mod(x, y *Uint) *Uint { + if x.IsZero() || y.IsZero() { + return z.Clear() + } + switch x.Cmp(y) { + case -1: + // x < y + copy(z.arr[:], x.arr[:]) + return z + case 0: + // x == y + return z.Clear() // They are equal + } + + // At this point: + // x != 0 + // y != 0 + // x > y + + // Shortcut trivial case + if x.IsUint64() { + return z.SetUint64(x.Uint64() % y.Uint64()) + } + + var quot Uint + *z = udivrem(quot.arr[:], x.arr[:], y) + return z +} + +// DivMod sets z to the quotient x div y and m to the modulus x mod y and returns the pair (z, m) for y != 0. +// If y == 0, both z and m are set to 0 (OBS: differs from the big.Int) +func (z *Uint) DivMod(x, y, m *Uint) (*Uint, *Uint) { + if y.IsZero() { + return z.Clear(), m.Clear() + } + var quot Uint + *m = udivrem(quot.arr[:], x.arr[:], y) + *z = quot + return z, m +} + +// udivrem divides u by d and produces both quotient and remainder. +// The quotient is stored in provided quot - len(u)-len(d)+1 words. +// It loosely follows the Knuth's division algorithm (sometimes referenced as "schoolbook" division) using 64-bit words. +// See Knuth, Volume 2, section 4.3.1, Algorithm D. +func udivrem(quot, u []uint64, d *Uint) (rem Uint) { + var dLen int + for i := len(d.arr) - 1; i >= 0; i-- { + if d.arr[i] != 0 { + dLen = i + 1 + break + } + } + + shift := uint(bits.LeadingZeros64(d.arr[dLen-1])) + + var dnStorage Uint + dn := dnStorage.arr[:dLen] + for i := dLen - 1; i > 0; i-- { + dn[i] = (d.arr[i] << shift) | (d.arr[i-1] >> (64 - shift)) + } + dn[0] = d.arr[0] << shift + + var uLen int + for i := len(u) - 1; i >= 0; i-- { + if u[i] != 0 { + uLen = i + 1 + break + } + } + + if uLen < dLen { + copy(rem.arr[:], u) + return rem + } + + var unStorage [9]uint64 + un := unStorage[:uLen+1] + un[uLen] = u[uLen-1] >> (64 - shift) + for i := uLen - 1; i > 0; i-- { + un[i] = (u[i] << shift) | (u[i-1] >> (64 - shift)) + } + un[0] = u[0] << shift + + // TODO: Skip the highest word of numerator if not significant. + + if dLen == 1 { + r := udivremBy1(quot, un, dn[0]) + rem.SetUint64(r >> shift) + return rem + } + + udivremKnuth(quot, un, dn) + + for i := 0; i < dLen-1; i++ { + rem.arr[i] = (un[i] >> shift) | (un[i+1] << (64 - shift)) + } + rem.arr[dLen-1] = un[dLen-1] >> shift + + return rem +} + +// umul computes full 256 x 256 -> 512 multiplication. +func umul(x, y *Uint) [8]uint64 { + var ( + res [8]uint64 + carry, carry4, carry5, carry6 uint64 + res1, res2, res3, res4, res5 uint64 + ) + + carry, res[0] = bits.Mul64(x.arr[0], y.arr[0]) + carry, res1 = umulHop(carry, x.arr[1], y.arr[0]) + carry, res2 = umulHop(carry, x.arr[2], y.arr[0]) + carry4, res3 = umulHop(carry, x.arr[3], y.arr[0]) + + carry, res[1] = umulHop(res1, x.arr[0], y.arr[1]) + carry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry) + carry, res3 = umulStep(res3, x.arr[2], y.arr[1], carry) + carry5, res4 = umulStep(carry4, x.arr[3], y.arr[1], carry) + + carry, res[2] = umulHop(res2, x.arr[0], y.arr[2]) + carry, res3 = umulStep(res3, x.arr[1], y.arr[2], carry) + carry, res4 = umulStep(res4, x.arr[2], y.arr[2], carry) + carry6, res5 = umulStep(carry5, x.arr[3], y.arr[2], carry) + + carry, res[3] = umulHop(res3, x.arr[0], y.arr[3]) + carry, res[4] = umulStep(res4, x.arr[1], y.arr[3], carry) + carry, res[5] = umulStep(res5, x.arr[2], y.arr[3], carry) + res[7], res[6] = umulStep(carry6, x.arr[3], y.arr[3], carry) + + return res +} + +// umulStep computes (hi * 2^64 + lo) = z + (x * y) + carry. +func umulStep(z, x, y, carry uint64) (hi, lo uint64) { + hi, lo = bits.Mul64(x, y) + lo, carry = bits.Add64(lo, carry, 0) + hi, _ = bits.Add64(hi, 0, carry) + lo, carry = bits.Add64(lo, z, 0) + hi, _ = bits.Add64(hi, 0, carry) + return hi, lo +} + +// umulHop computes (hi * 2^64 + lo) = z + (x * y) +func umulHop(z, x, y uint64) (hi, lo uint64) { + hi, lo = bits.Mul64(x, y) + lo, carry := bits.Add64(lo, z, 0) + hi, _ = bits.Add64(hi, 0, carry) + return hi, lo +} + +// udivremBy1 divides u by single normalized word d and produces both quotient and remainder. +// The quotient is stored in provided quot. +func udivremBy1(quot, u []uint64, d uint64) (rem uint64) { + reciprocal := reciprocal2by1(d) + rem = u[len(u)-1] // Set the top word as remainder. + for j := len(u) - 2; j >= 0; j-- { + quot[j], rem = udivrem2by1(rem, u[j], d, reciprocal) + } + return rem +} + +// udivremKnuth implements the division of u by normalized multiple word d from the Knuth's division algorithm. +// The quotient is stored in provided quot - len(u)-len(d) words. +// Updates u to contain the remainder - len(d) words. +func udivremKnuth(quot, u, d []uint64) { + dh := d[len(d)-1] + dl := d[len(d)-2] + reciprocal := reciprocal2by1(dh) + + for j := len(u) - len(d) - 1; j >= 0; j-- { + u2 := u[j+len(d)] + u1 := u[j+len(d)-1] + u0 := u[j+len(d)-2] + + var qhat, rhat uint64 + if u2 >= dh { // Division overflows. + qhat = ^uint64(0) + // TODO: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case). + } else { + qhat, rhat = udivrem2by1(u2, u1, dh, reciprocal) + ph, pl := bits.Mul64(qhat, dl) + if ph > rhat || (ph == rhat && pl > u0) { + qhat-- + // TODO: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case). + } + } + + // Multiply and subtract. + borrow := subMulTo(u[j:], d, qhat) + u[j+len(d)] = u2 - borrow + if u2 < borrow { // Too much subtracted, add back. + qhat-- + u[j+len(d)] += addTo(u[j:], d) + } + + quot[j] = qhat // Store quotient digit. + } +} + +// isBitSet returns true if bit n-th is set, where n = 0 is LSB. +// The n must be <= 255. +func (z *Uint) isBitSet(n uint) bool { + return (z.arr[n/64] & (1 << (n % 64))) != 0 +} + +// addTo computes x += y. +// Requires len(x) >= len(y). +func addTo(x, y []uint64) uint64 { + var carry uint64 + for i := 0; i < len(y); i++ { + x[i], carry = bits.Add64(x[i], y[i], carry) + } + return carry +} + +// subMulTo computes x -= y * multiplier. +// Requires len(x) >= len(y). +func subMulTo(x, y []uint64, multiplier uint64) uint64 { + var borrow uint64 + for i := 0; i < len(y); i++ { + s, carry1 := bits.Sub64(x[i], borrow, 0) + ph, pl := bits.Mul64(y[i], multiplier) + t, carry2 := bits.Sub64(s, pl, 0) + x[i] = t + borrow = ph + carry1 + carry2 + } + return borrow +} + +// reciprocal2by1 computes <^d, ^0> / d. +func reciprocal2by1(d uint64) uint64 { + reciprocal, _ := bits.Div64(^d, ^uint64(0), d) + return reciprocal +} + +// udivrem2by1 divides / d and produces both quotient and remainder. +// It uses the provided d's reciprocal. +// Implementation ported from https://github.com/chfast/intx and is based on +// "Improved division by invariant integers", Algorithm 4. +func udivrem2by1(uh, ul, d, reciprocal uint64) (quot, rem uint64) { + qh, ql := bits.Mul64(reciprocal, uh) + ql, carry := bits.Add64(ql, ul, 0) + qh, _ = bits.Add64(qh, uh, carry) + qh++ + + r := ul - qh*d + + if r > ql { + qh-- + r += d + } + + if r >= d { + qh++ + r -= d + } + + return qh, r +} \ No newline at end of file diff --git a/examples/gno.land/p/demo/uint256/arithmetic_test.gno b/examples/gno.land/p/demo/uint256/arithmetic_test.gno new file mode 100644 index 00000000000..633bdf73f14 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/arithmetic_test.gno @@ -0,0 +1,182 @@ +package uint256 + +import ( + "testing" +) + +type binOpTest struct { + x, y, want string +} + +func TestAdd(t *testing.T) { + tests := []binOpTest{ + {"0", "1", "1"}, + {"1", "0", "1"}, + {"1", "1", "2"}, + {"1", "3", "4"}, + {"10", "10", "20"}, + } + + for _, tc := range tests { + x, err := parseUintFromString(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := parseUintFromString(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := parseUintFromString(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Add(x, y) + + for i := range got.arr { + if got.arr[i] != want.arr[i] { + t.Errorf("Add(%s, %s) = %v, want %v", tc.x, tc.y, got, want) + break + } + } + } +} + +// TODO: need to test negative results +func TestSub(t *testing.T) { + tests := []binOpTest{ + {"1", "0", "1"}, + {"1", "1", "0"}, + {"10", "10", "0"}, + } + + for _, tc := range tests { + x, err := parseUintFromString(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := parseUintFromString(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := parseUintFromString(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Sub(x, y) + + for i := range got.arr { + if got.arr[i] != want.arr[i] { + t.Errorf("Sub(%s, %s) = %v, want %v", tc.x, tc.y, got, want) + break + } + } + } +} + +func TestMul(t *testing.T) { + tests := []binOpTest{ + {"1", "0", "0"}, + {"1", "1", "1"}, + {"10", "10", "100"}, + } + + for _, tc := range tests { + x, err := parseUintFromString(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := parseUintFromString(tc.y) + if err != nil { + t.Error(err) + continue + } + + want, err := parseUintFromString(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Mul(x, y) + + for i := range got.arr { + if got.arr[i] != want.arr[i] { + t.Errorf("Mul(%s, %s) = %v, want %v", tc.x, tc.y, got, want) + break + } + } + } +} + +func TestDivMod(t *testing.T) { + tests := []struct { + x string + y string + wantDiv string + wantMod string + }{ + {"1", "1", "1", "0"}, + {"10", "10", "1", "0"}, + {"100", "10", "10", "0"}, + } + + for _, tc := range tests { + x, err := parseUintFromString(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := parseUintFromString(tc.y) + if err != nil { + t.Error(err) + continue + } + + wantDiv, err := parseUintFromString(tc.wantDiv) + if err != nil { + t.Error(err) + continue + } + + wantMod, err := parseUintFromString(tc.wantMod) + if err != nil { + t.Error(err) + continue + } + + gotDiv := new(Uint) + gotMod := new(Uint) + gotDiv.DivMod(x, y, gotMod) + + for i := range gotDiv.arr { + if gotDiv.arr[i] != wantDiv.arr[i] { + t.Errorf("DivMod(%s, %s) got Div %v, want Div %v", tc.x, tc.y, gotDiv, wantDiv) + break + } + } + for i := range gotMod.arr { + if gotMod.arr[i] != wantMod.arr[i] { + t.Errorf("DivMod(%s, %s) got Mod %v, want Mod %v", tc.x, tc.y, gotMod, wantMod) + break + } + } + } +} diff --git a/examples/gno.land/p/demo/uint256/bitwise.gno b/examples/gno.land/p/demo/uint256/bitwise.gno new file mode 100644 index 00000000000..90905e2f48f --- /dev/null +++ b/examples/gno.land/p/demo/uint256/bitwise.gno @@ -0,0 +1,263 @@ +// bitwise contains bitwise operations for Uint instances. +// This file includes functions to perform bitwise AND, OR, XOR, and NOT operations, as well as bit shifting. +// These operations are crucial for manipulating individual bits within a 256-bit unsigned integer. +package uint256 + +// Or sets z = x | y and returns z. +func (z *Uint) Or(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] | y.arr[0] + z.arr[1] = x.arr[1] | y.arr[1] + z.arr[2] = x.arr[2] | y.arr[2] + z.arr[3] = x.arr[3] | y.arr[3] + return z +} + +// And sets z = x & y and returns z. +func (z *Uint) And(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] & y.arr[0] + z.arr[1] = x.arr[1] & y.arr[1] + z.arr[2] = x.arr[2] & y.arr[2] + z.arr[3] = x.arr[3] & y.arr[3] + return z +} + +// Not sets z = ^x and returns z. +func (z *Uint) Not(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = ^x.arr[3], ^x.arr[2], ^x.arr[1], ^x.arr[0] + return z +} + +func (z *Uint) AndNot(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] &^ y.arr[0] + z.arr[1] = x.arr[1] &^ y.arr[1] + z.arr[2] = x.arr[2] &^ y.arr[2] + z.arr[3] = x.arr[3] &^ y.arr[3] + return z +} + +// Xor sets z = x ^ y and returns z. +func (z *Uint) Xor(x, y *Uint) *Uint { + z.arr[0] = x.arr[0] ^ y.arr[0] + z.arr[1] = x.arr[1] ^ y.arr[1] + z.arr[2] = x.arr[2] ^ y.arr[2] + z.arr[3] = x.arr[3] ^ y.arr[3] + return z +} + +// Lsh sets z = x << n and returns z. +func (z *Uint) Lsh(x *Uint, n uint) *Uint { + // n % 64 == 0 + if n&0x3f == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.lsh64(x) + case 128: + return z.lsh128(x) + case 192: + return z.lsh192(x) + default: + return z.Clear() + } + } + var a, b uint64 + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.Clear() + } + z.lsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.lsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.lsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + a = z.arr[0] >> (64 - n) + z.arr[0] = z.arr[0] << n + +sh64: + b = z.arr[1] >> (64 - n) + z.arr[1] = (z.arr[1] << n) | a + +sh128: + a = z.arr[2] >> (64 - n) + z.arr[2] = (z.arr[2] << n) | b + +sh192: + z.arr[3] = (z.arr[3] << n) | a + + return z +} + +// Rsh sets z = x >> n and returns z. +func (z *Uint) Rsh(x *Uint, n uint) *Uint { + // n % 64 == 0 + if n&0x3f == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.rsh64(x) + case 128: + return z.rsh128(x) + case 192: + return z.rsh192(x) + default: + return z.Clear() + } + } + var a, b uint64 + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.Clear() + } + z.rsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.rsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.rsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + a = z.arr[3] << (64 - n) + z.arr[3] = z.arr[3] >> n + +sh64: + b = z.arr[2] << (64 - n) + z.arr[2] = (z.arr[2] >> n) | a + +sh128: + a = z.arr[1] << (64 - n) + z.arr[1] = (z.arr[1] >> n) | b + +sh192: + z.arr[0] = (z.arr[0] >> n) | a + + return z +} + +// SRsh (Signed/Arithmetic right shift) +// considers z to be a signed integer, during right-shift +// and sets z = x >> n and returns z. +func (z *Uint) SRsh(x *Uint, n uint) *Uint { + // If the MSB is 0, SRsh is same as Rsh. + if !x.isBitSet(255) { + return z.Rsh(x, n) + } + if n%64 == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.srsh64(x) + case 128: + return z.srsh128(x) + case 192: + return z.srsh192(x) + default: + return z.SetAllOne() + } + } + var a uint64 = MaxUint64 << (64 - n%64) + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.SetAllOne() + } + z.srsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.srsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.srsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + z.arr[3], a = (z.arr[3]>>n)|a, z.arr[3]<<(64-n) + +sh64: + z.arr[2], a = (z.arr[2]>>n)|a, z.arr[2]<<(64-n) + +sh128: + z.arr[1], a = (z.arr[1]>>n)|a, z.arr[1]<<(64-n) + +sh192: + z.arr[0] = (z.arr[0] >> n) | a + + return z +} + +func (z *Uint) lsh64(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[2], x.arr[1], x.arr[0], 0 + return z +} + +func (z *Uint) lsh128(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[1], x.arr[0], 0, 0 + return z +} + +func (z *Uint) lsh192(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[0], 0, 0, 0 + return z +} + +func (z *Uint) rsh64(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, x.arr[3], x.arr[2], x.arr[1] + return z +} + +func (z *Uint) rsh128(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, x.arr[3], x.arr[2] + return z +} + +func (z *Uint) rsh192(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x.arr[3] + return z +} + +func (z *Uint) srsh64(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, x.arr[3], x.arr[2], x.arr[1] + return z +} + +func (z *Uint) srsh128(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, x.arr[3], x.arr[2] + return z +} + +func (z *Uint) srsh192(x *Uint) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, x.arr[3] + return z +} diff --git a/examples/gno.land/p/demo/uint256/bitwise_test.gno b/examples/gno.land/p/demo/uint256/bitwise_test.gno new file mode 100644 index 00000000000..d03fd03dfd5 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/bitwise_test.gno @@ -0,0 +1,64 @@ +package uint256 + +import ( + "testing" +) + +type logicOpTest struct { + x, y, want string +} + +func TestAndBitForm(t *testing.T) { + testCases := []struct { + name string + x Uint + y Uint + want Uint + }{ + { + name: "all zeros", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "mixed", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "mixed 2", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "one operand zero", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "one operand all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + want: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + res := new(Uint).And(&tc.x, &tc.y) + if *res != tc.want { + t.Errorf("And(%v, %v) = %v, want %v", tc.x, tc.y, *res, tc.want) + } + }) + } +} diff --git a/examples/gno.land/p/demo/uint256/cmp.gno b/examples/gno.land/p/demo/uint256/cmp.gno new file mode 100644 index 00000000000..c31706ac1d5 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/cmp.gno @@ -0,0 +1,120 @@ +// cmp (or, comparisons) includes methods for comparing Uint instances. +// These comparison functions cover a range of operations including equality checks, less than/greater than +// evaluations, and specialized comparisons such as signed greater than. These are fundamental for logical +// decision making based on Uint values. +package uint256 + +import ( + "math/bits" +) + +// Cmp compares z and x and returns: +// +// -1 if z < x +// 0 if z == x +// +1 if z > x +func (z *Uint) Cmp(x *Uint) (r int) { + // z < x <=> z - x < 0 i.e. when subtraction overflows. + d0, carry := bits.Sub64(z.arr[0], x.arr[0], 0) + d1, carry := bits.Sub64(z.arr[1], x.arr[1], carry) + d2, carry := bits.Sub64(z.arr[2], x.arr[2], carry) + d3, carry := bits.Sub64(z.arr[3], x.arr[3], carry) + if carry == 1 { + return -1 + } + if d0|d1|d2|d3 == 0 { + return 0 + } + return 1 +} + +// IsZero returns true if z == 0 +func (z *Uint) IsZero() bool { + return (z.arr[0] | z.arr[1] | z.arr[2] | z.arr[3]) == 0 +} + +// Sign returns: +// +// -1 if z < 0 +// 0 if z == 0 +// +1 if z > 0 +// +// Where z is interpreted as a two's complement signed number +func (z *Uint) Sign() int { + if z.IsZero() { + return 0 + } + if z.arr[3] < 0x8000000000000000 { + return 1 + } + return -1 +} + +// LtUint64 returns true if z is smaller than n +func (z *Uint) LtUint64(n uint64) bool { + return z.arr[0] < n && (z.arr[1]|z.arr[2]|z.arr[3]) == 0 +} + +// GtUint64 returns true if z is larger than n +func (z *Uint) GtUint64(n uint64) bool { + return z.arr[0] > n || (z.arr[1]|z.arr[2]|z.arr[3]) != 0 +} + +// Lt returns true if z < x +func (z *Uint) Lt(x *Uint) bool { + // z < x <=> z - x < 0 i.e. when subtraction overflows. + _, carry := bits.Sub64(z.arr[0], x.arr[0], 0) + _, carry = bits.Sub64(z.arr[1], x.arr[1], carry) + _, carry = bits.Sub64(z.arr[2], x.arr[2], carry) + _, carry = bits.Sub64(z.arr[3], x.arr[3], carry) + + return carry != 0 +} + +// Gt returns true if z > x +func (z *Uint) Gt(x *Uint) bool { + return x.Lt(z) +} + +// Lte returns true if z <= x +func (z *Uint) Lte(x *Uint) bool { + cond1 := z.Lt(x) + cond2 := z.Eq(x) + + if cond1 || cond2 { + return true + } + return false +} + +// Gte returns true if z >= x +func (z *Uint) Gte(x *Uint) bool { + cond1 := z.Gt(x) + cond2 := z.Eq(x) + + if cond1 || cond2 { + return true + } + return false +} + +// Eq returns true if z == x +func (z *Uint) Eq(x *Uint) bool { + return (z.arr[0] == x.arr[0]) && (z.arr[1] == x.arr[1]) && (z.arr[2] == x.arr[2]) && (z.arr[3] == x.arr[3]) +} + +// Sgt interprets z and x as signed integers, and returns +// true if z > x +func (z *Uint) Sgt(x *Uint) bool { + zSign := z.Sign() + xSign := x.Sign() + + switch { + case zSign >= 0 && xSign < 0: + return true + case zSign < 0 && xSign >= 0: + return false + default: + return z.Gt(x) + } +} \ No newline at end of file diff --git a/examples/gno.land/p/demo/uint256/cmp_test.gno b/examples/gno.land/p/demo/uint256/cmp_test.gno new file mode 100644 index 00000000000..8ae2f720ac2 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/cmp_test.gno @@ -0,0 +1,102 @@ +package uint256 + +import ( + "testing" +) + +func TestCmp(t *testing.T) { + tests := []struct { + x, y string + want int + }{ + {"0", "0", 0}, + {"0", "1", -1}, + {"1", "0", 1}, + {"1", "1", 0}, + {"10", "10", 0}, + {"10", "11", -1}, + {"11", "10", 1}, + } + + for _, tc := range tests { + x, err := parseUintFromString(tc.x) + if err != nil { + t.Error(err) + continue + } + + y, err := parseUintFromString(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 TestLtUint64(t *testing.T) { + tests := []struct { + x string + y uint64 + want bool + }{ + {"0", 1, true}, + {"1", 0, false}, + {"10", 10, false}, + {"ffffffffffffffff", 0, false}, + {"10000000000000000", 0, false}, + } + + for _, tc := range tests { + x := new(Uint).SetBytes(hex2Bytes(tc.x)) + got := x.LtUint64(tc.y) + + if got != tc.want { + t.Errorf("LtUint64(%s, %d) = %v, want %v", tc.x, tc.y, got, tc.want) + } + } +} + +func TestIsZero(t *testing.T) { + tests := []struct { + x string + want bool + }{ + {"0", true}, + {"1", false}, + {"10", false}, + } + + for _, tc := range tests { + x, err := parseUintFromString(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 TestSGT(t *testing.T) { + x := new(Uint).SetBytes(hex2Bytes("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")) + y := new(Uint).SetBytes(hex2Bytes("00")) + actual := x.Sgt(y) + if actual { + t.Fatalf("Expected %v false", actual) + } + + x = new(Uint).SetBytes(hex2Bytes("00")) + y = new(Uint).SetBytes(hex2Bytes("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")) + actual = x.Sgt(y) + if !actual { + t.Fatalf("Expected %v true", actual) + } +} diff --git a/examples/gno.land/p/demo/uint256/conversion.gno b/examples/gno.land/p/demo/uint256/conversion.gno index 2262b1733db..5576ae1635d 100644 --- a/examples/gno.land/p/demo/uint256/conversion.gno +++ b/examples/gno.land/p/demo/uint256/conversion.gno @@ -1,15 +1,216 @@ +// conversions contains methods for converting Uint instances to other types and vice versa. +// This includes conversions to and from basic types such as uint64 and int32, as well as string representations +// and byte slices. Additionally, it covers marshaling and unmarshaling for JSON and other text formats. package uint256 import ( "encoding/binary" + "strconv" ) +// Uint64 returns the lower 64-bits of z +func (z *Uint) Uint64() uint64 { + return z.arr[0] +} + +// Uint64WithOverflow returns the lower 64-bits of z and bool whether overflow occurred +func (z *Uint) Uint64WithOverflow() (uint64, bool) { + return z.arr[0], (z.arr[1] | z.arr[2] | z.arr[3]) != 0 +} + +// SetUint64 sets z to the value x +func (z *Uint) SetUint64(x uint64) *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x + return z +} + +// IsUint64 reports whether z can be represented as a uint64. +func (z *Uint) IsUint64() bool { + return (z.arr[1] | z.arr[2] | z.arr[3]) == 0 +} + +// Dec returns the decimal representation of z. +func (z *Uint) Dec() string { + if z.IsZero() { + return "0" + } + if z.IsUint64() { + return strconv.FormatUint(z.Uint64(), 10) + } + + // The max uint64 value being 18446744073709551615, the largest + // power-of-ten below that is 10000000000000000000. + // When we do a DivMod using that number, the remainder that we + // get back is the lower part of the output. + // + // The ascii-output of remainder will never exceed 19 bytes (since it will be + // below 10000000000000000000). + // + // Algorithm example using 100 as divisor + // + // 12345 % 100 = 45 (rem) + // 12345 / 100 = 123 (quo) + // -> output '45', continue iterate on 123 + var ( + // out is 98 bytes long: 78 (max size of a string without leading zeroes, + // plus slack so we can copy 19 bytes every iteration). + // We init it with zeroes, because when strconv appends the ascii representations, + // it will omit leading zeroes. + out = []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + divisor = NewUint(10000000000000000000) // 20 digits + y = new(Uint).Set(z) // copy to avoid modifying z + pos = len(out) // position to write to + buf = make([]byte, 0, 19) // buffer to write uint64:s to + ) + for { + // Obtain Q and R for divisor + var quot Uint + rem := udivrem(quot.arr[:], y.arr[:], divisor) + y.Set(") // Set Q for next loop + // Convert the R to ascii representation + buf = strconv.AppendUint(buf[:0], rem.Uint64(), 10) + // Copy in the ascii digits + copy(out[pos-len(buf):], buf) + if y.IsZero() { + break + } + // Move 19 digits left + pos -= 19 + } + // skip leading zeroes by only using the 'used size' of buf + return string(out[pos-len(buf):]) +} + +// ToString returns the decimal string representation of z. It returns an empty string if z is nil. +func (z *Uint) ToString() string { + if z == nil { + return "" + } + + return z.Dec() +} + +// MarshalJSON implements json.Marshaler. +// MarshalJSON marshals using the 'decimal string' representation. This is _not_ compatible +// with big.Uint: big.Uint marshals into JSON 'native' numeric format. +// +// The JSON native format is, on some platforms, (e.g. javascript), limited to 53-bit large +// integer space. Thus, U256 uses string-format, which is not compatible with +// big.int (big.Uint refuses to unmarshal a string representation). +func (z *Uint) MarshalJSON() ([]byte, error) { + return []byte(`"` + z.Dec() + `"`), nil +} + +// UnmarshalJSON implements json.Unmarshaler. UnmarshalJSON accepts either +// - Quoted string: either hexadecimal OR decimal +// - Not quoted string: only decimal +func (z *Uint) UnmarshalJSON(input []byte) error { + if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { + // if not quoted, it must be decimal + return z.fromDecimal(string(input)) + } + return z.UnmarshalText(input[1 : len(input)-1]) +} + +// MarshalText implements encoding.TextMarshaler +// MarshalText marshals using the decimal representation (compatible with big.Uint) +func (z *Uint) MarshalText() ([]byte, error) { + return []byte(z.Dec()), nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. This method +// can unmarshal either hexadecimal or decimal. +// - For hexadecimal, the input _must_ be prefixed with 0x or 0X +func (z *Uint) UnmarshalText(input []byte) error { + if len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') { + return z.fromHex(string(input)) + } + return z.fromDecimal(string(input)) +} + +// SetBytes interprets buf as the bytes of a big-endian unsigned +// integer, sets z to that value, and returns z. +// If buf is larger than 32 bytes, the last 32 bytes is used. +func (z *Uint) SetBytes(buf []byte) *Uint { + switch l := len(buf); l { + case 0: + z.Clear() + case 1: + z.SetBytes1(buf) + case 2: + z.SetBytes2(buf) + case 3: + z.SetBytes3(buf) + case 4: + z.SetBytes4(buf) + case 5: + z.SetBytes5(buf) + case 6: + z.SetBytes6(buf) + case 7: + z.SetBytes7(buf) + case 8: + z.SetBytes8(buf) + case 9: + z.SetBytes9(buf) + case 10: + z.SetBytes10(buf) + case 11: + z.SetBytes11(buf) + case 12: + z.SetBytes12(buf) + case 13: + z.SetBytes13(buf) + case 14: + z.SetBytes14(buf) + case 15: + z.SetBytes15(buf) + case 16: + z.SetBytes16(buf) + case 17: + z.SetBytes17(buf) + case 18: + z.SetBytes18(buf) + case 19: + z.SetBytes19(buf) + case 20: + z.SetBytes20(buf) + case 21: + z.SetBytes21(buf) + case 22: + z.SetBytes22(buf) + case 23: + z.SetBytes23(buf) + case 24: + z.SetBytes24(buf) + case 25: + z.SetBytes25(buf) + case 26: + z.SetBytes26(buf) + case 27: + z.SetBytes27(buf) + case 28: + z.SetBytes28(buf) + case 29: + z.SetBytes29(buf) + case 30: + z.SetBytes30(buf) + case 31: + z.SetBytes31(buf) + default: + z.SetBytes32(buf[l-32:]) + } + return z +} + +// SetBytes1 is identical to SetBytes(in[:1]), but panics is input is too short func (z *Uint) SetBytes1(in []byte) *Uint { z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 z.arr[0] = uint64(in[0]) return z } +// SetBytes2 is identical to SetBytes(in[:2]), but panics is input is too short func (z *Uint) SetBytes2(in []byte) *Uint { _ = in[1] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 @@ -17,6 +218,7 @@ func (z *Uint) SetBytes2(in []byte) *Uint { return z } +// SetBytes3 is identical to SetBytes(in[:3]), but panics is input is too short func (z *Uint) SetBytes3(in []byte) *Uint { _ = in[2] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 @@ -24,6 +226,7 @@ func (z *Uint) SetBytes3(in []byte) *Uint { return z } +// SetBytes4 is identical to SetBytes(in[:4]), but panics is input is too short func (z *Uint) SetBytes4(in []byte) *Uint { _ = in[3] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 @@ -31,6 +234,7 @@ func (z *Uint) SetBytes4(in []byte) *Uint { return z } +// SetBytes5 is identical to SetBytes(in[:5]), but panics is input is too short func (z *Uint) SetBytes5(in []byte) *Uint { _ = in[4] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 @@ -38,6 +242,7 @@ func (z *Uint) SetBytes5(in []byte) *Uint { return z } +// SetBytes6 is identical to SetBytes(in[:6]), but panics is input is too short func (z *Uint) SetBytes6(in []byte) *Uint { _ = in[5] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 @@ -45,6 +250,7 @@ func (z *Uint) SetBytes6(in []byte) *Uint { return z } +// SetBytes7 is identical to SetBytes(in[:7]), but panics is input is too short func (z *Uint) SetBytes7(in []byte) *Uint { _ = in[6] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 @@ -60,6 +266,7 @@ func (z *Uint) SetBytes8(in []byte) *Uint { return z } +// SetBytes9 is identical to SetBytes(in[:9]), but panics is input is too short func (z *Uint) SetBytes9(in []byte) *Uint { _ = in[8] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2] = 0, 0 @@ -68,6 +275,7 @@ func (z *Uint) SetBytes9(in []byte) *Uint { return z } +// SetBytes10 is identical to SetBytes(in[:10]), but panics is input is too short func (z *Uint) SetBytes10(in []byte) *Uint { _ = in[9] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2] = 0, 0 @@ -76,6 +284,7 @@ func (z *Uint) SetBytes10(in []byte) *Uint { return z } +// SetBytes11 is identical to SetBytes(in[:11]), but panics is input is too short func (z *Uint) SetBytes11(in []byte) *Uint { _ = in[10] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2] = 0, 0 @@ -84,7 +293,7 @@ func (z *Uint) SetBytes11(in []byte) *Uint { return z } - +// SetBytes12 is identical to SetBytes(in[:12]), but panics is input is too short func (z *Uint) SetBytes12(in []byte) *Uint { _ = in[11] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2] = 0, 0 @@ -93,6 +302,7 @@ func (z *Uint) SetBytes12(in []byte) *Uint { return z } +// SetBytes13 is identical to SetBytes(in[:13]), but panics is input is too short func (z *Uint) SetBytes13(in []byte) *Uint { _ = in[12] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2] = 0, 0 @@ -101,6 +311,7 @@ func (z *Uint) SetBytes13(in []byte) *Uint { return z } +// SetBytes14 is identical to SetBytes(in[:14]), but panics is input is too short func (z *Uint) SetBytes14(in []byte) *Uint { _ = in[13] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2] = 0, 0 @@ -109,6 +320,7 @@ func (z *Uint) SetBytes14(in []byte) *Uint { return z } +// SetBytes15 is identical to SetBytes(in[:15]), but panics is input is too short func (z *Uint) SetBytes15(in []byte) *Uint { _ = in[14] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3], z.arr[2] = 0, 0 @@ -126,6 +338,7 @@ func (z *Uint) SetBytes16(in []byte) *Uint { return z } +// SetBytes17 is identical to SetBytes(in[:17]), but panics is input is too short func (z *Uint) SetBytes17(in []byte) *Uint { _ = in[16] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = 0 @@ -135,6 +348,7 @@ func (z *Uint) SetBytes17(in []byte) *Uint { return z } +// SetBytes18 is identical to SetBytes(in[:18]), but panics is input is too short func (z *Uint) SetBytes18(in []byte) *Uint { _ = in[17] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = 0 @@ -144,6 +358,7 @@ func (z *Uint) SetBytes18(in []byte) *Uint { return z } +// SetBytes19 is identical to SetBytes(in[:19]), but panics is input is too short func (z *Uint) SetBytes19(in []byte) *Uint { _ = in[18] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = 0 @@ -153,6 +368,7 @@ func (z *Uint) SetBytes19(in []byte) *Uint { return z } +// SetBytes20 is identical to SetBytes(in[:20]), but panics is input is too short func (z *Uint) SetBytes20(in []byte) *Uint { _ = in[19] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = 0 @@ -162,6 +378,7 @@ func (z *Uint) SetBytes20(in []byte) *Uint { return z } +// SetBytes21 is identical to SetBytes(in[:21]), but panics is input is too short func (z *Uint) SetBytes21(in []byte) *Uint { _ = in[20] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = 0 @@ -171,6 +388,7 @@ func (z *Uint) SetBytes21(in []byte) *Uint { return z } +// SetBytes22 is identical to SetBytes(in[:22]), but panics is input is too short func (z *Uint) SetBytes22(in []byte) *Uint { _ = in[21] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = 0 @@ -180,6 +398,7 @@ func (z *Uint) SetBytes22(in []byte) *Uint { return z } +// SetBytes23 is identical to SetBytes(in[:23]), but panics is input is too short func (z *Uint) SetBytes23(in []byte) *Uint { _ = in[22] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = 0 @@ -199,6 +418,7 @@ func (z *Uint) SetBytes24(in []byte) *Uint { return z } +// SetBytes25 is identical to SetBytes(in[:25]), but panics is input is too short func (z *Uint) SetBytes25(in []byte) *Uint { _ = in[24] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = uint64(in[0]) @@ -208,6 +428,7 @@ func (z *Uint) SetBytes25(in []byte) *Uint { return z } +// SetBytes26 is identical to SetBytes(in[:26]), but panics is input is too short func (z *Uint) SetBytes26(in []byte) *Uint { _ = in[25] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = uint64(binary.BigEndian.Uint16(in[0:2])) @@ -217,6 +438,7 @@ func (z *Uint) SetBytes26(in []byte) *Uint { return z } +// SetBytes27 is identical to SetBytes(in[:27]), but panics is input is too short func (z *Uint) SetBytes27(in []byte) *Uint { _ = in[26] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = uint64(binary.BigEndian.Uint16(in[1:3])) | uint64(in[0])<<16 @@ -226,6 +448,7 @@ func (z *Uint) SetBytes27(in []byte) *Uint { return z } +// SetBytes28 is identical to SetBytes(in[:28]), but panics is input is too short func (z *Uint) SetBytes28(in []byte) *Uint { _ = in[27] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = uint64(binary.BigEndian.Uint32(in[0:4])) @@ -235,6 +458,7 @@ func (z *Uint) SetBytes28(in []byte) *Uint { return z } +// SetBytes29 is identical to SetBytes(in[:29]), but panics is input is too short func (z *Uint) SetBytes29(in []byte) *Uint { _ = in[23] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = bigEndianUint40(in[0:5]) @@ -244,7 +468,7 @@ func (z *Uint) SetBytes29(in []byte) *Uint { return z } - +// SetBytes30 is identical to SetBytes(in[:30]), but panics is input is too short func (z *Uint) SetBytes30(in []byte) *Uint { _ = in[29] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = bigEndianUint48(in[0:6]) @@ -254,6 +478,7 @@ func (z *Uint) SetBytes30(in []byte) *Uint { return z } +// SetBytes31 is identical to SetBytes(in[:31]), but panics is input is too short func (z *Uint) SetBytes31(in []byte) *Uint { _ = in[30] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = bigEndianUint56(in[0:7]) @@ -263,6 +488,7 @@ func (z *Uint) SetBytes31(in []byte) *Uint { return z } +// SetBytes32 sets z to the value of the big-endian 256-bit unsigned integer in. func (z *Uint) SetBytes32(in []byte) *Uint { _ = in[31] // bounds check hint to compiler; see golang.org/issue/14808 z.arr[3] = binary.BigEndian.Uint64(in[0:8]) @@ -274,18 +500,21 @@ func (z *Uint) SetBytes32(in []byte) *Uint { // Utility methods that are "missing" among the bigEndian.UintXX methods. +// bigEndianUint40 returns the uint64 value represented by the 5 bytes in big-endian order. func bigEndianUint40(b []byte) uint64 { _ = b[4] // bounds check hint to compiler; see golang.org/issue/14808 return uint64(b[4]) | uint64(b[3])<<8 | uint64(b[2])<<16 | uint64(b[1])<<24 | uint64(b[0])<<32 } +// bigEndianUint56 returns the uint64 value represented by the 7 bytes in big-endian order. func bigEndianUint56(b []byte) uint64 { _ = b[6] // bounds check hint to compiler; see golang.org/issue/14808 return uint64(b[6]) | uint64(b[5])<<8 | uint64(b[4])<<16 | uint64(b[3])<<24 | uint64(b[2])<<32 | uint64(b[1])<<40 | uint64(b[0])<<48 } +// bigEndianUint48 returns the uint64 value represented by the 6 bytes in big-endian order. func bigEndianUint48(b []byte) uint64 { _ = b[5] // bounds check hint to compiler; see golang.org/issue/14808 return uint64(b[5]) | uint64(b[4])<<8 | uint64(b[3])<<16 | uint64(b[2])<<24 | diff --git a/examples/gno.land/p/demo/uint256/conversion_test.gno b/examples/gno.land/p/demo/uint256/conversion_test.gno new file mode 100644 index 00000000000..7969e8c8d94 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/conversion_test.gno @@ -0,0 +1,60 @@ +package uint256 + +import ( + "testing" +) + +func TestIsUint64(t *testing.T) { + tests := []struct { + x string + want bool + }{ + {"0", true}, + {"1", true}, + {"10", true}, + {"ffffffffffffffff", true}, + {"10000000000000000", true}, + } + + for _, tc := range tests { + x := new(Uint).SetBytes(hex2Bytes(tc.x)) + got := x.IsUint64() + + if got != tc.want { + t.Errorf("IsUint64(%s) = %v, want %v", tc.x, got, tc.want) + } + } +} + +func TestDec(t *testing.T) { + testCases := []struct { + name string + z Uint + want string + }{ + { + name: "zero", + z: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: "0", + }, + { + name: "less than 20 digits", + z: Uint{arr: [4]uint64{1234567890, 0, 0, 0}}, + want: "1234567890", + }, + { + name: "max possible value", + z: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: "115792089237316195423570985008687907853269984665640564039457584007913129639935", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := tc.z.Dec() + if result != tc.want { + t.Errorf("Dec(%v) = %s, want %s", tc.z, result, tc.want) + } + }) + } +} diff --git a/examples/gno.land/p/demo/uint256/error.gno b/examples/gno.land/p/demo/uint256/error.gno new file mode 100644 index 00000000000..ef7363ff3e4 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/error.gno @@ -0,0 +1,73 @@ +package uint256 + +import ( + "errors" +) + +var ( + ErrEmptyString = errors.New("empty hex string") + ErrSyntax = errors.New("invalid hex string") + ErrRange = errors.New("number out of range") + ErrMissingPrefix = errors.New("hex string without 0x prefix") + ErrEmptyNumber = errors.New("hex string \"0x\"") + ErrLeadingZero = errors.New("hex number with leading zero digits") + ErrBig256Range = errors.New("hex number > 256 bits") + ErrBadBufferLength = errors.New("bad ssz buffer length") + ErrBadEncodedLength = errors.New("bad ssz encoded length") + ErrInvalidBase = errors.New("invalid base") + ErrInvalidBitSize = errors.New("invalid bit size") +) + +type u256Error struct { + fn string // function name + input string + err error +} + +func (e *u256Error) Error() string { + return e.fn + ": " + e.input + ": " + e.err.Error() +} + +func (e *u256Error) Unwrap() error { + return e.err +} + +func errEmptyString(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrEmptyString} +} + +func errSyntax(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrSyntax} +} + +func errMissingPrefix(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrMissingPrefix} +} + +func errEmptyNumber(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrEmptyNumber} +} + +func errLeadingZero(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrLeadingZero} +} + +func errRange(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrRange} +} + +func errBig256Range(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrBig256Range} +} + +func errBadBufferLength(fn, input string) error { + return &u256Error{fn: fn, input: input, err: ErrBadBufferLength} +} + +func errInvalidBase(fn string, base int) error { + return &u256Error{fn: fn, input: string(base), err: ErrInvalidBase} +} + +func errInvalidBitSize(fn string, bitSize int) error { + return &u256Error{fn: fn, input: string(bitSize), err: ErrInvalidBitSize} +} \ No newline at end of file diff --git a/examples/gno.land/p/demo/uint256/uint256.gno b/examples/gno.land/p/demo/uint256/uint256.gno index 088100eca36..8175736aa72 100644 --- a/examples/gno.land/p/demo/uint256/uint256.gno +++ b/examples/gno.land/p/demo/uint256/uint256.gno @@ -12,21 +12,6 @@ const ( uintSize = 32 << (^uint(0) >> 63) ) -func Zero() *Uint { - return NewUint(0) -} - -func One() *Uint { - return NewUint(1) -} - -func (x *Uint) Min(y *Uint) *Uint { - if x.Lt(y) { - return x - } - return y -} - // Uint is represented as an array of 4 uint64, in little-endian order, // so that Uint[3] is the most significant, and Uint[0] is the least significant type Uint struct { @@ -39,72 +24,14 @@ func NewUint(val uint64) *Uint { return z } -// Uint64 returns the lower 64-bits of z -func (z *Uint) Uint64() uint64 { - return z.arr[0] -} - -// Uint64WithOverflow returns the lower 64-bits of z and bool whether overflow occurred -func (z *Uint) Uint64WithOverflow() (uint64, bool) { - return z.arr[0], (z.arr[1] | z.arr[2] | z.arr[3]) != 0 -} - -func (z *Uint) Int32() int32 { - x := z.arr[0] - if x > 0x7fffffff { - panic("U256 Int32 overflow") - } - return int32(x) -} - -// SetUint64 sets z to the value x -func (z *Uint) SetUint64(x uint64) *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x - return z -} - -// LtUint64 returns true if z is smaller than n -func (z *Uint) LtUint64(n uint64) bool { - return z.arr[0] < n && (z.arr[1]|z.arr[2]|z.arr[3]) == 0 -} - -// GtUint64 returns true if z is larger than n -func (z *Uint) GtUint64(n uint64) bool { - return z.arr[0] > n || (z.arr[1]|z.arr[2]|z.arr[3]) != 0 -} - -// IsUint64 reports whether z can be represented as a uint64. -func (z *Uint) IsUint64() bool { - return (z.arr[1] | z.arr[2] | z.arr[3]) == 0 -} - -// IsZero returns true if z == 0 -func (z *Uint) IsZero() bool { - return (z.arr[0] | z.arr[1] | z.arr[2] | z.arr[3]) == 0 -} - -// Sign returns: -// -// -1 if z < 0 -// 0 if z == 0 -// +1 if z > 0 -// -// Where z is interpreted as a two's complement signed number -func (z *Uint) Sign() int { - if z.IsZero() { - return 0 - } - if z.arr[3] < 0x8000000000000000 { - return 1 - } - return -1 +// Zero returns a new Uint initialized to zero. +func Zero() *Uint { + return NewUint(0) } - -// Clear sets z to 0 -func (z *Uint) Clear() *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 0 - return z +// One returns a new Uint initialized to one. +func One() *Uint { + return NewUint(1) } // SetAllOne sets all the bits of z to 1 @@ -113,96 +40,6 @@ func (z *Uint) SetAllOne() *Uint { return z } -// Neg returns -x mod 2**256. -func (z *Uint) Neg(x *Uint) *Uint { - return z.Sub(new(Uint), x) -} - -// Not sets z = ^x and returns z. -func (z *Uint) Not(x *Uint) *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = ^x.arr[3], ^x.arr[2], ^x.arr[1], ^x.arr[0] - return z -} - -// Sgt interprets z and x as signed integers, and returns -// true if z > x -func (z *Uint) Sgt(x *Uint) bool { - zSign := z.Sign() - xSign := x.Sign() - - switch { - case zSign >= 0 && xSign < 0: - return true - case zSign < 0 && xSign >= 0: - return false - default: - return z.Gt(x) - } -} - -// Lt returns true if z < x -func (z *Uint) Lt(x *Uint) bool { - // z < x <=> z - x < 0 i.e. when subtraction overflows. - _, carry := bits.Sub64(z.arr[0], x.arr[0], 0) - _, carry = bits.Sub64(z.arr[1], x.arr[1], carry) - _, carry = bits.Sub64(z.arr[2], x.arr[2], carry) - _, carry = bits.Sub64(z.arr[3], x.arr[3], carry) - - return carry != 0 -} - -// Gt returns true if z > x -func (z *Uint) Gt(x *Uint) bool { - return x.Lt(z) -} - -// Lte returns true if z <= x -func (z *Uint) Lte(x *Uint) bool { - cond1 := z.Lt(x) - cond2 := z.Eq(x) - - if cond1 || cond2 { - return true - } - return false -} - -// Gte returns true if z >= x -func (z *Uint) Gte(x *Uint) bool { - cond1 := z.Gt(x) - cond2 := z.Eq(x) - - if cond1 || cond2 { - return true - } - return false -} - -// Eq returns true if z == x -func (z *Uint) Eq(x *Uint) bool { - return (z.arr[0] == x.arr[0]) && (z.arr[1] == x.arr[1]) && (z.arr[2] == x.arr[2]) && (z.arr[3] == x.arr[3]) -} - -// Cmp compares z and x and returns: -// -// -1 if z < x -// 0 if z == x -// +1 if z > x -func (z *Uint) Cmp(x *Uint) (r int) { - // z < x <=> z - x < 0 i.e. when subtraction overflows. - d0, carry := bits.Sub64(z.arr[0], x.arr[0], 0) - d1, carry := bits.Sub64(z.arr[1], x.arr[1], carry) - d2, carry := bits.Sub64(z.arr[2], x.arr[2], carry) - d3, carry := bits.Sub64(z.arr[3], x.arr[3], carry) - if carry == 1 { - return -1 - } - if d0|d1|d2|d3 == 0 { - return 0 - } - return 1 -} - // Set sets z to x and returns z. func (z *Uint) Set(x *Uint) *Uint { *z = *x @@ -216,712 +53,6 @@ func (z *Uint) SetOne() *Uint { return z } -// Add sets z to the sum x+y -func (z *Uint) Add(x, y *Uint) *Uint { - var carry uint64 - z.arr[0], carry = bits.Add64(x.arr[0], y.arr[0], 0) - z.arr[1], carry = bits.Add64(x.arr[1], y.arr[1], carry) - z.arr[2], carry = bits.Add64(x.arr[2], y.arr[2], carry) - z.arr[3], _ = bits.Add64(x.arr[3], y.arr[3], carry) - return z -} - -// AddOverflow sets z to the sum x+y, and returns z and whether overflow occurred -func (z *Uint) AddOverflow(x, y *Uint) (*Uint, bool) { - var carry uint64 - z.arr[0], carry = bits.Add64(x.arr[0], y.arr[0], 0) - z.arr[1], carry = bits.Add64(x.arr[1], y.arr[1], carry) - z.arr[2], carry = bits.Add64(x.arr[2], y.arr[2], carry) - z.arr[3], carry = bits.Add64(x.arr[3], y.arr[3], carry) - return z, carry != 0 -} - -// SubOverflow sets z to the difference x-y and returns z and true if the operation underflowed -func (z *Uint) SubOverflow(x, y *Uint) (*Uint, bool) { - var carry uint64 - z.arr[0], carry = bits.Sub64(x.arr[0], y.arr[0], 0) - z.arr[1], carry = bits.Sub64(x.arr[1], y.arr[1], carry) - z.arr[2], carry = bits.Sub64(x.arr[2], y.arr[2], carry) - z.arr[3], carry = bits.Sub64(x.arr[3], y.arr[3], carry) - return z, carry != 0 -} - -// Sub sets z to the difference x-y -func (z *Uint) Sub(x, y *Uint) *Uint { - var carry uint64 - z.arr[0], carry = bits.Sub64(x.arr[0], y.arr[0], 0) - z.arr[1], carry = bits.Sub64(x.arr[1], y.arr[1], carry) - z.arr[2], carry = bits.Sub64(x.arr[2], y.arr[2], carry) - z.arr[3], _ = bits.Sub64(x.arr[3], y.arr[3], carry) - return z -} - -// commented out for possible overflow -// Mul sets z to the product x*y -func (z *Uint) Mul(x, y *Uint) *Uint { - var ( - res Uint - carry uint64 - res1, res2, res3 uint64 - ) - - carry, res.arr[0] = bits.Mul64(x.arr[0], y.arr[0]) - carry, res1 = umulHop(carry, x.arr[1], y.arr[0]) - carry, res2 = umulHop(carry, x.arr[2], y.arr[0]) - res3 = x.arr[3]*y.arr[0] + carry - - carry, res.arr[1] = umulHop(res1, x.arr[0], y.arr[1]) - carry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry) - res3 = res3 + x.arr[2]*y.arr[1] + carry - - carry, res.arr[2] = umulHop(res2, x.arr[0], y.arr[2]) - res3 = res3 + x.arr[1]*y.arr[2] + carry - - res.arr[3] = res3 + x.arr[0]*y.arr[3] - - return z.Set(&res) -} - -// MulOverflow sets z to the product x*y, and returns z and whether overflow occurred -func (z *Uint) MulOverflow(x, y *Uint) (*Uint, bool) { - p := umul(x, y) - copy(z.arr[:], p[:4]) - return z, (p[4] | p[5] | p[6] | p[7]) != 0 -} - -// MulMod calculates the modulo-m multiplication of x and y and -// returns z. -// If m == 0, z is set to 0 (OBS: differs from the big.Int) -func (z *Uint) MulMod(x, y, m *Uint) *Uint { - if x.IsZero() || y.IsZero() || m.IsZero() { - return z.Clear() - } - p := umul(x, y) - - if m.arr[3] != 0 { - mu := Reciprocal(m) - r := reduce4(p, m, mu) - return z.Set(&r) - } - - var ( - pl Uint - ph Uint - ) - - pl = Uint{arr: [4]uint64{p[0], p[1], p[2], p[3]}} - ph = Uint{arr: [4]uint64{p[4], p[5], p[6], p[7]}} - - // If the multiplication is within 256 bits use Mod(). - if ph.IsZero() { - return z.Mod(&pl, m) - } - - var quot [8]uint64 - rem := udivrem(quot[:], p[:], m) - return z.Set(&rem) -} - -// umulStep computes (hi * 2^64 + lo) = z + (x * y) + carry. -func umulStep(z, x, y, carry uint64) (hi, lo uint64) { - hi, lo = bits.Mul64(x, y) - lo, carry = bits.Add64(lo, carry, 0) - hi, _ = bits.Add64(hi, 0, carry) - lo, carry = bits.Add64(lo, z, 0) - hi, _ = bits.Add64(hi, 0, carry) - return hi, lo -} - -// umulHop computes (hi * 2^64 + lo) = z + (x * y) -func umulHop(z, x, y uint64) (hi, lo uint64) { - hi, lo = bits.Mul64(x, y) - lo, carry := bits.Add64(lo, z, 0) - hi, _ = bits.Add64(hi, 0, carry) - return hi, lo -} - -// umul computes full 256 x 256 -> 512 multiplication. -func umul(x, y *Uint) [8]uint64 { - var ( - res [8]uint64 - carry, carry4, carry5, carry6 uint64 - res1, res2, res3, res4, res5 uint64 - ) - - carry, res[0] = bits.Mul64(x.arr[0], y.arr[0]) - carry, res1 = umulHop(carry, x.arr[1], y.arr[0]) - carry, res2 = umulHop(carry, x.arr[2], y.arr[0]) - carry4, res3 = umulHop(carry, x.arr[3], y.arr[0]) - - carry, res[1] = umulHop(res1, x.arr[0], y.arr[1]) - carry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry) - carry, res3 = umulStep(res3, x.arr[2], y.arr[1], carry) - carry5, res4 = umulStep(carry4, x.arr[3], y.arr[1], carry) - - carry, res[2] = umulHop(res2, x.arr[0], y.arr[2]) - carry, res3 = umulStep(res3, x.arr[1], y.arr[2], carry) - carry, res4 = umulStep(res4, x.arr[2], y.arr[2], carry) - carry6, res5 = umulStep(carry5, x.arr[3], y.arr[2], carry) - - carry, res[3] = umulHop(res3, x.arr[0], y.arr[3]) - carry, res[4] = umulStep(res4, x.arr[1], y.arr[3], carry) - carry, res[5] = umulStep(res5, x.arr[2], y.arr[3], carry) - res[7], res[6] = umulStep(carry6, x.arr[3], y.arr[3], carry) - - return res -} - -// commented out for possible overflow -// Div sets z to the quotient x/y for returns z. -// If y == 0, z is set to 0 -func (z *Uint) Div(x, y *Uint) *Uint { - if y.IsZero() || y.Gt(x) { - return z.Clear() - } - if x.Eq(y) { - return z.SetOne() - } - // Shortcut some cases - if x.IsUint64() { - return z.SetUint64(x.Uint64() / y.Uint64()) - } - - // At this point, we know - // x/y ; x > y > 0 - - var quot Uint - udivrem(quot.arr[:], x.arr[:], y) - return z.Set(") -} - -// udivrem divides u by d and produces both quotient and remainder. -// The quotient is stored in provided quot - len(u)-len(d)+1 words. -// It loosely follows the Knuth's division algorithm (sometimes referenced as "schoolbook" division) using 64-bit words. -// See Knuth, Volume 2, section 4.3.1, Algorithm D. -func udivrem(quot, u []uint64, d *Uint) (rem Uint) { - var dLen int - for i := len(d.arr) - 1; i >= 0; i-- { - if d.arr[i] != 0 { - dLen = i + 1 - break - } - } - - shift := uint(bits.LeadingZeros64(d.arr[dLen-1])) - - var dnStorage Uint - dn := dnStorage.arr[:dLen] - for i := dLen - 1; i > 0; i-- { - dn[i] = (d.arr[i] << shift) | (d.arr[i-1] >> (64 - shift)) - } - dn[0] = d.arr[0] << shift - - var uLen int - for i := len(u) - 1; i >= 0; i-- { - if u[i] != 0 { - uLen = i + 1 - break - } - } - - if uLen < dLen { - copy(rem.arr[:], u) - return rem - } - - var unStorage [9]uint64 - un := unStorage[:uLen+1] - un[uLen] = u[uLen-1] >> (64 - shift) - for i := uLen - 1; i > 0; i-- { - un[i] = (u[i] << shift) | (u[i-1] >> (64 - shift)) - } - un[0] = u[0] << shift - - // TODO: Skip the highest word of numerator if not significant. - - if dLen == 1 { - r := udivremBy1(quot, un, dn[0]) - rem.SetUint64(r >> shift) - return rem - } - - udivremKnuth(quot, un, dn) - - for i := 0; i < dLen-1; i++ { - rem.arr[i] = (un[i] >> shift) | (un[i+1] << (64 - shift)) - } - rem.arr[dLen-1] = un[dLen-1] >> shift - - return rem -} - -// udivremBy1 divides u by single normalized word d and produces both quotient and remainder. -// The quotient is stored in provided quot. -func udivremBy1(quot, u []uint64, d uint64) (rem uint64) { - reciprocal := reciprocal2by1(d) - rem = u[len(u)-1] // Set the top word as remainder. - for j := len(u) - 2; j >= 0; j-- { - quot[j], rem = udivrem2by1(rem, u[j], d, reciprocal) - } - return rem -} - -// udivremKnuth implements the division of u by normalized multiple word d from the Knuth's division algorithm. -// The quotient is stored in provided quot - len(u)-len(d) words. -// Updates u to contain the remainder - len(d) words. -func udivremKnuth(quot, u, d []uint64) { - dh := d[len(d)-1] - dl := d[len(d)-2] - reciprocal := reciprocal2by1(dh) - - for j := len(u) - len(d) - 1; j >= 0; j-- { - u2 := u[j+len(d)] - u1 := u[j+len(d)-1] - u0 := u[j+len(d)-2] - - var qhat, rhat uint64 - if u2 >= dh { // Division overflows. - qhat = ^uint64(0) - // TODO: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case). - } else { - qhat, rhat = udivrem2by1(u2, u1, dh, reciprocal) - ph, pl := bits.Mul64(qhat, dl) - if ph > rhat || (ph == rhat && pl > u0) { - qhat-- - // TODO: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case). - } - } - - // Multiply and subtract. - borrow := subMulTo(u[j:], d, qhat) - u[j+len(d)] = u2 - borrow - if u2 < borrow { // Too much subtracted, add back. - qhat-- - u[j+len(d)] += addTo(u[j:], d) - } - - quot[j] = qhat // Store quotient digit. - } -} - -// isBitSet returns true if bit n-th is set, where n = 0 is LSB. -// The n must be <= 255. -func (z *Uint) isBitSet(n uint) bool { - return (z.arr[n/64] & (1 << (n % 64))) != 0 -} - -// addTo computes x += y. -// Requires len(x) >= len(y). -func addTo(x, y []uint64) uint64 { - var carry uint64 - for i := 0; i < len(y); i++ { - x[i], carry = bits.Add64(x[i], y[i], carry) - } - return carry -} - -// subMulTo computes x -= y * multiplier. -// Requires len(x) >= len(y). -func subMulTo(x, y []uint64, multiplier uint64) uint64 { - var borrow uint64 - for i := 0; i < len(y); i++ { - s, carry1 := bits.Sub64(x[i], borrow, 0) - ph, pl := bits.Mul64(y[i], multiplier) - t, carry2 := bits.Sub64(s, pl, 0) - x[i] = t - borrow = ph + carry1 + carry2 - } - return borrow -} - -// reciprocal2by1 computes <^d, ^0> / d. -func reciprocal2by1(d uint64) uint64 { - reciprocal, _ := bits.Div64(^d, ^uint64(0), d) - return reciprocal -} - -// udivrem2by1 divides / d and produces both quotient and remainder. -// It uses the provided d's reciprocal. -// Implementation ported from https://github.com/chfast/intx and is based on -// "Improved division by invariant integers", Algorithm 4. -func udivrem2by1(uh, ul, d, reciprocal uint64) (quot, rem uint64) { - qh, ql := bits.Mul64(reciprocal, uh) - ql, carry := bits.Add64(ql, ul, 0) - qh, _ = bits.Add64(qh, uh, carry) - qh++ - - r := ul - qh*d - - if r > ql { - qh-- - r += d - } - - if r >= d { - qh++ - r -= d - } - - return qh, r -} - -// Lsh sets z = x << n and returns z. -func (z *Uint) Lsh(x *Uint, n uint) *Uint { - // n % 64 == 0 - if n&0x3f == 0 { - switch n { - case 0: - return z.Set(x) - case 64: - return z.lsh64(x) - case 128: - return z.lsh128(x) - case 192: - return z.lsh192(x) - default: - return z.Clear() - } - } - var a, b uint64 - // Big swaps first - switch { - case n > 192: - if n > 256 { - return z.Clear() - } - z.lsh192(x) - n -= 192 - goto sh192 - case n > 128: - z.lsh128(x) - n -= 128 - goto sh128 - case n > 64: - z.lsh64(x) - n -= 64 - goto sh64 - default: - z.Set(x) - } - - // remaining shifts - a = z.arr[0] >> (64 - n) - z.arr[0] = z.arr[0] << n - -sh64: - b = z.arr[1] >> (64 - n) - z.arr[1] = (z.arr[1] << n) | a - -sh128: - a = z.arr[2] >> (64 - n) - z.arr[2] = (z.arr[2] << n) | b - -sh192: - z.arr[3] = (z.arr[3] << n) | a - - return z -} - -// Rsh sets z = x >> n and returns z. -func (z *Uint) Rsh(x *Uint, n uint) *Uint { - // n % 64 == 0 - if n&0x3f == 0 { - switch n { - case 0: - return z.Set(x) - case 64: - return z.rsh64(x) - case 128: - return z.rsh128(x) - case 192: - return z.rsh192(x) - default: - return z.Clear() - } - } - var a, b uint64 - // Big swaps first - switch { - case n > 192: - if n > 256 { - return z.Clear() - } - z.rsh192(x) - n -= 192 - goto sh192 - case n > 128: - z.rsh128(x) - n -= 128 - goto sh128 - case n > 64: - z.rsh64(x) - n -= 64 - goto sh64 - default: - z.Set(x) - } - - // remaining shifts - a = z.arr[3] << (64 - n) - z.arr[3] = z.arr[3] >> n - -sh64: - b = z.arr[2] << (64 - n) - z.arr[2] = (z.arr[2] >> n) | a - -sh128: - a = z.arr[1] << (64 - n) - z.arr[1] = (z.arr[1] >> n) | b - -sh192: - z.arr[0] = (z.arr[0] >> n) | a - - return z -} - -// SRsh (Signed/Arithmetic right shift) -// considers z to be a signed integer, during right-shift -// and sets z = x >> n and returns z. -func (z *Uint) SRsh(x *Uint, n uint) *Uint { - // If the MSB is 0, SRsh is same as Rsh. - if !x.isBitSet(255) { - return z.Rsh(x, n) - } - if n%64 == 0 { - switch n { - case 0: - return z.Set(x) - case 64: - return z.srsh64(x) - case 128: - return z.srsh128(x) - case 192: - return z.srsh192(x) - default: - return z.SetAllOne() - } - } - var a uint64 = MaxUint64 << (64 - n%64) - // Big swaps first - switch { - case n > 192: - if n > 256 { - return z.SetAllOne() - } - z.srsh192(x) - n -= 192 - goto sh192 - case n > 128: - z.srsh128(x) - n -= 128 - goto sh128 - case n > 64: - z.srsh64(x) - n -= 64 - goto sh64 - default: - z.Set(x) - } - - // remaining shifts - z.arr[3], a = (z.arr[3]>>n)|a, z.arr[3]<<(64-n) - -sh64: - z.arr[2], a = (z.arr[2]>>n)|a, z.arr[2]<<(64-n) - -sh128: - z.arr[1], a = (z.arr[1]>>n)|a, z.arr[1]<<(64-n) - -sh192: - z.arr[0] = (z.arr[0] >> n) | a - - return z -} - -func (z *Uint) lsh64(x *Uint) *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[2], x.arr[1], x.arr[0], 0 - return z -} - -func (z *Uint) lsh128(x *Uint) *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[1], x.arr[0], 0, 0 - return z -} - -func (z *Uint) lsh192(x *Uint) *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[0], 0, 0, 0 - return z -} - -func (z *Uint) rsh64(x *Uint) *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, x.arr[3], x.arr[2], x.arr[1] - return z -} - -func (z *Uint) rsh128(x *Uint) *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, x.arr[3], x.arr[2] - return z -} - -func (z *Uint) rsh192(x *Uint) *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x.arr[3] - return z -} - -func (z *Uint) srsh64(x *Uint) *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, x.arr[3], x.arr[2], x.arr[1] - return z -} - -func (z *Uint) srsh128(x *Uint) *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, x.arr[3], x.arr[2] - return z -} - -func (z *Uint) srsh192(x *Uint) *Uint { - z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, x.arr[3] - return z -} - -// Or sets z = x | y and returns z. -func (z *Uint) Or(x, y *Uint) *Uint { - z.arr[0] = x.arr[0] | y.arr[0] - z.arr[1] = x.arr[1] | y.arr[1] - z.arr[2] = x.arr[2] | y.arr[2] - z.arr[3] = x.arr[3] | y.arr[3] - return z -} - -// And sets z = x & y and returns z. -func (z *Uint) And(x, y *Uint) *Uint { - z.arr[0] = x.arr[0] & y.arr[0] - z.arr[1] = x.arr[1] & y.arr[1] - z.arr[2] = x.arr[2] & y.arr[2] - z.arr[3] = x.arr[3] & y.arr[3] - return z -} - -// Xor sets z = x ^ y and returns z. -func (z *Uint) Xor(x, y *Uint) *Uint { - z.arr[0] = x.arr[0] ^ y.arr[0] - z.arr[1] = x.arr[1] ^ y.arr[1] - z.arr[2] = x.arr[2] ^ y.arr[2] - z.arr[3] = x.arr[3] ^ y.arr[3] - return z -} - -// Byte sets z to the value of the byte at position n, -// with 'z' considered as a big-endian 32-byte integer -// if 'n' > 32, f is set to 0 -// Example: f = '5', n=31 => 5 -func (z *Uint) Byte(n *Uint) *Uint { - // in z, z.arr[0] is the least significant - if number, overflow := n.Uint64WithOverflow(); !overflow { - if number < 32 { - number := z.arr[4-1-number/8] - offset := (n.arr[0] & 0x7) << 3 // 8*(n.d % 8) - z.arr[0] = (number & (0xff00000000000000 >> offset)) >> (56 - offset) - z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 - return z - } - } - - return z.Clear() -} - -// MarshalJSON implements json.Marshaler. -// MarshalJSON marshals using the 'decimal string' representation. This is _not_ compatible -// with big.Uint: big.Uint marshals into JSON 'native' numeric format. -// -// The JSON native format is, on some platforms, (e.g. javascript), limited to 53-bit large -// integer space. Thus, U256 uses string-format, which is not compatible with -// big.int (big.Uint refuses to unmarshal a string representation). -func (z *Uint) MarshalJSON() ([]byte, error) { - return []byte(`"` + z.Dec() + `"`), nil -} - -// UnmarshalJSON implements json.Unmarshaler. UnmarshalJSON accepts either -// - Quoted string: either hexadecimal OR decimal -// - Not quoted string: only decimal -func (z *Uint) UnmarshalJSON(input []byte) error { - if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { - // if not quoted, it must be decimal - return z.fromDecimal(string(input)) - } - return z.UnmarshalText(input[1 : len(input)-1]) -} - -// MarshalText implements encoding.TextMarshaler -// MarshalText marshals using the decimal representation (compatible with big.Uint) -func (z *Uint) MarshalText() ([]byte, error) { - return []byte(z.Dec()), nil -} - -// UnmarshalText implements encoding.TextUnmarshaler. This method -// can unmarshal either hexadecimal or decimal. -// - For hexadecimal, the input _must_ be prefixed with 0x or 0X -func (z *Uint) UnmarshalText(input []byte) error { - if len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') { - return z.fromHex(string(input)) - } - return z.fromDecimal(string(input)) -} - -const ( - hextable = "0123456789abcdef" - bintable = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x06\a\b\t\xff\xff\xff\xff\xff\xff\xff\n\v\f\r\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\n\v\f\r\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - badNibble = 0xff -) - -// fromHex is the internal implementation of parsing a hex-string. -func (z *Uint) fromHex(hex string) error { - if err := checkNumberS(hex); err != nil { - return err - } - if len(hex) > 66 { - return ErrBig256Range - } - z.Clear() - end := len(hex) - for i := 0; i < 4; i++ { - start := end - 16 - if start < 2 { - start = 2 - } - for ri := start; ri < end; ri++ { - nib := bintable[hex[ri]] - if nib == badNibble { - return ErrSyntax - } - z.arr[i] = z.arr[i] << 4 - z.arr[i] += uint64(nib) - } - end = start - } - return nil -} - -// FromDecimal is a convenience-constructor to create an Uint from a -// decimal (base 10) string. Numbers larger than 256 bits are not accepted. -func FromDecimal(decimal string) (*Uint, error) { - var z Uint - if err := z.SetFromDecimal(decimal); err != nil { - return nil, err - } - return &z, nil -} - -// MustFromDecimal is a convenience-constructor to create an Int from a -// decimal (base 10) string. -// Returns a new Int and panics if any error occurred. -func MustFromDecimal(decimal string) *Uint { - var z Uint - if err := z.SetFromDecimal(decimal); err != nil { - panic(err) - } - return &z -} - const twoPow256Sub1 = "115792089237316195423570985008687907853269984665640564039457584007913129639935" // SetFromDecimal sets z from the given string, interpreted as a decimal number. @@ -958,33 +89,14 @@ func (z *Uint) SetFromDecimal(s string) (err error) { return ErrBig256Range } -var ( - ErrEmptyString = errors.New("empty hex string") - ErrSyntax = errors.New("invalid hex string") - ErrMissingPrefix = errors.New("hex string without 0x prefix") - ErrEmptyNumber = errors.New("hex string \"0x\"") - ErrLeadingZero = errors.New("hex number with leading zero digits") - ErrBig256Range = errors.New("hex number > 256 bits") - ErrBadBufferLength = errors.New("bad ssz buffer length") - ErrBadEncodedLength = errors.New("bad ssz encoded length") -) - -func checkNumberS(input string) error { - l := len(input) - if l == 0 { - return ErrEmptyString - } - if l < 2 || input[0] != '0' || - (input[1] != 'x' && input[1] != 'X') { - return ErrMissingPrefix - } - if l == 2 { - return ErrEmptyNumber - } - if len(input) > 3 && input[2] == '0' { - return ErrLeadingZero +// FromDecimal is a convenience-constructor to create an Uint from a +// decimal (base 10) string. Numbers larger than 256 bits are not accepted. +func FromDecimal(decimal string) (*Uint, error) { + var z Uint + if err := z.SetFromDecimal(decimal); err != nil { + return nil, err } - return nil + return &z, nil } // multipliers holds the values that are needed for fromDecimal @@ -1044,267 +156,63 @@ func (z *Uint) fromDecimal(bs string) error { return nil } -// lower(c) is a lower-case letter if and only if -// c is either that lower-case letter or the equivalent upper-case letter. -// Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'. -// Note that lower of non-letters can produce other non-letters. -func lower(c byte) byte { - return c | ('x' - 'X') -} - -// ParseUint is like ParseUint but for unsigned numbers. -// -// A sign prefix is not permitted. -func parseUint(s string, base int, bitSize int) (uint64, error) { - const fnParseUint = "ParseUint" - - if s == "" { - return 0, errors.New("syntax error: ParseUint empty string") - } - - base0 := base == 0 - - s0 := s - switch { - case 2 <= base && base <= 36: - // valid base; nothing to do - - case base == 0: - // Look for octal, hex prefix. - base = 10 - if s[0] == '0' { - switch { - case len(s) >= 3 && lower(s[1]) == 'b': - base = 2 - s = s[2:] - case len(s) >= 3 && lower(s[1]) == 'o': - base = 8 - s = s[2:] - case len(s) >= 3 && lower(s[1]) == 'x': - base = 16 - s = s[2:] - default: - base = 8 - s = s[1:] - } - } - - default: - return 0, errors.New("invalid base") - } - - if bitSize == 0 { - bitSize = uintSize - } else if bitSize < 0 || bitSize > 64 { - return 0, errors.New("invalid bit size") - } - - // Cutoff is the smallest number such that cutoff*base > maxUint64. - // Use compile-time constants for common cases. - var cutoff uint64 - switch base { - case 10: - cutoff = MaxUint64/10 + 1 - case 16: - cutoff = MaxUint64/16 + 1 - default: - cutoff = MaxUint64/uint64(base) + 1 - } - - maxVal := uint64(1)<= byte(base) { - return 0, errors.New("syntax error") - } - - if n >= cutoff { - // n*base overflows - return maxVal, errors.New("range error") - } - n *= uint64(base) - - n1 := n + uint64(d) - if n1 < n || n1 > maxVal { - // n+d overflows - return maxVal, errors.New("range error") +// Byte sets z to the value of the byte at position n, +// with 'z' considered as a big-endian 32-byte integer +// if 'n' > 32, f is set to 0 +// Example: f = '5', n=31 => 5 +func (z *Uint) Byte(n *Uint) *Uint { + // in z, z.arr[0] is the least significant + if number, overflow := n.Uint64WithOverflow(); !overflow { + if number < 32 { + number := z.arr[4-1-number/8] + offset := (n.arr[0] & 0x7) << 3 // 8*(n.d % 8) + z.arr[0] = (number & (0xff00000000000000 >> offset)) >> (56 - offset) + z.arr[3], z.arr[2], z.arr[1] = 0, 0, 0 + return z } - n = n1 } - if underscores && !underscoreOK(s0) { - return 0, errors.New("syntax error") - } + return z.Clear() +} - return n, nil +// Clear sets z to 0 +func (z *Uint) Clear() *Uint { + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 0 + return z } -// underscoreOK reports whether the underscores in s are allowed. -// Checking them in this one function lets all the parsers skip over them simply. -// Underscore must appear only between digits or between a base prefix and a digit. -func underscoreOK(s string) bool { - // saw tracks the last character (class) we saw: - // ^ for beginning of number, - // 0 for a digit or base prefix, - // _ for an underscore, - // ! for none of the above. - saw := '^' - i := 0 +const ( + // hextable = "0123456789abcdef" + bintable = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x06\a\b\t\xff\xff\xff\xff\xff\xff\xff\n\v\f\r\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\n\v\f\r\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + badNibble = 0xff +) - // Optional sign. - if len(s) >= 1 && (s[0] == '-' || s[0] == '+') { - s = s[1:] +// fromHex is the internal implementation of parsing a hex-string. +func (z *Uint) fromHex(hex string) error { + if err := checkNumberS(hex); err != nil { + return err } - - // Optional base prefix. - hex := false - if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') { - i = 2 - saw = '0' // base prefix counts as a digit for "underscore as digit separator" - hex = lower(s[1]) == 'x' + if len(hex) > 66 { + return ErrBig256Range } - - // Number proper. - for ; i < len(s); i++ { - // Digits are always okay. - if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' { - saw = '0' - continue + z.Clear() + end := len(hex) + for i := 0; i < 4; i++ { + start := end - 16 + if start < 2 { + start = 2 } - // Underscore must follow digit. - if s[i] == '_' { - if saw != '0' { - return false + for ri := start; ri < end; ri++ { + nib := bintable[hex[ri]] + if nib == badNibble { + return ErrSyntax } - saw = '_' - continue - } - // Underscore must also be followed by digit. - if saw == '_' { - return false - } - // Saw non-digit, non-underscore. - saw = '!' - } - return saw != '_' -} - -// Dec returns the decimal representation of z. -func (z *Uint) Dec() string { - if z.IsZero() { - return "0" - } - if z.IsUint64() { - return strconv.FormatUint(z.Uint64(), 10) - } - - // The max uint64 value being 18446744073709551615, the largest - // power-of-ten below that is 10000000000000000000. - // When we do a DivMod using that number, the remainder that we - // get back is the lower part of the output. - // - // The ascii-output of remainder will never exceed 19 bytes (since it will be - // below 10000000000000000000). - // - // Algorithm example using 100 as divisor - // - // 12345 % 100 = 45 (rem) - // 12345 / 100 = 123 (quo) - // -> output '45', continue iterate on 123 - var ( - // out is 98 bytes long: 78 (max size of a string without leading zeroes, - // plus slack so we can copy 19 bytes every iteration). - // We init it with zeroes, because when strconv appends the ascii representations, - // it will omit leading zeroes. - out = []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") - divisor = NewUint(10000000000000000000) // 20 digits - y = new(Uint).Set(z) // copy to avoid modifying z - pos = len(out) // position to write to - buf = make([]byte, 0, 19) // buffer to write uint64:s to - ) - for { - // Obtain Q and R for divisor - var quot Uint - rem := udivrem(quot.arr[:], y.arr[:], divisor) - y.Set(") // Set Q for next loop - // Convert the R to ascii representation - buf = strconv.AppendUint(buf[:0], rem.Uint64(), 10) - // Copy in the ascii digits - copy(out[pos-len(buf):], buf) - if y.IsZero() { - break + z.arr[i] = z.arr[i] << 4 + z.arr[i] += uint64(nib) } - // Move 19 digits left - pos -= 19 - } - // skip leading zeroes by only using the 'used size' of buf - return string(out[pos-len(buf):]) -} - -func (z *Uint) ToString() string { - if z == nil { - panic("U256 ToString() nil") - } - - return z.Dec() -} - -// 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.Uint) -func (z *Uint) Mod(x, y *Uint) *Uint { - if x.IsZero() || y.IsZero() { - return z.Clear() - } - switch x.Cmp(y) { - case -1: - // x < y - copy(z.arr[:], x.arr[:]) - return z - case 0: - // x == y - return z.Clear() // They are equal - } - - // At this point: - // x != 0 - // y != 0 - // x > y - - // Shortcut trivial case - if x.IsUint64() { - return z.SetUint64(x.Uint64() % y.Uint64()) - } - - var quot Uint - *z = udivrem(quot.arr[:], x.arr[:], y) - return z -} - -// DivMod sets z to the quotient x div y and m to the modulus x mod y and returns the pair (z, m) for y != 0. -// If y == 0, both z and m are set to 0 (OBS: differs from the big.Int) -func (z *Uint) DivMod(x, y, m *Uint) (*Uint, *Uint) { - if y.IsZero() { - return z.Clear(), m.Clear() + end = start } - var quot Uint - *m = udivrem(quot.arr[:], x.arr[:], y) - *z = quot - return z, m + return nil } // Clone creates a new Int identical to z @@ -1329,87 +237,3 @@ func (z *Uint) NilToZero() *Uint { return z } - -func (z *Uint) AndNot(x, y *Uint) *Uint { - z.arr[0] = x.arr[0] &^ y.arr[0] - z.arr[1] = x.arr[1] &^ y.arr[1] - z.arr[2] = x.arr[2] &^ y.arr[2] - z.arr[3] = x.arr[3] &^ y.arr[3] - return z -} - - -// SetBytes interprets buf as the bytes of a big-endian unsigned -// integer, sets z to that value, and returns z. -// If buf is larger than 32 bytes, the last 32 bytes is used. -func (z *Uint) SetBytes(buf []byte) *Uint { - switch l := len(buf); l { - case 0: - z.Clear() - case 1: - z.SetBytes1(buf) - case 2: - z.SetBytes2(buf) - case 3: - z.SetBytes3(buf) - case 4: - z.SetBytes4(buf) - case 5: - z.SetBytes5(buf) - case 6: - z.SetBytes6(buf) - case 7: - z.SetBytes7(buf) - case 8: - z.SetBytes8(buf) - case 9: - z.SetBytes9(buf) - case 10: - z.SetBytes10(buf) - case 11: - z.SetBytes11(buf) - case 12: - z.SetBytes12(buf) - case 13: - z.SetBytes13(buf) - case 14: - z.SetBytes14(buf) - case 15: - z.SetBytes15(buf) - case 16: - z.SetBytes16(buf) - case 17: - z.SetBytes17(buf) - case 18: - z.SetBytes18(buf) - case 19: - z.SetBytes19(buf) - case 20: - z.SetBytes20(buf) - case 21: - z.SetBytes21(buf) - case 22: - z.SetBytes22(buf) - case 23: - z.SetBytes23(buf) - case 24: - z.SetBytes24(buf) - case 25: - z.SetBytes25(buf) - case 26: - z.SetBytes26(buf) - case 27: - z.SetBytes27(buf) - case 28: - z.SetBytes28(buf) - case 29: - z.SetBytes29(buf) - case 30: - z.SetBytes30(buf) - case 31: - z.SetBytes31(buf) - default: - z.SetBytes32(buf[l-32:]) - } - return z -} diff --git a/examples/gno.land/p/demo/uint256/uint256_test.gno b/examples/gno.land/p/demo/uint256/uint256_test.gno index 3f56014be69..04ca85e66ea 100644 --- a/examples/gno.land/p/demo/uint256/uint256_test.gno +++ b/examples/gno.land/p/demo/uint256/uint256_test.gno @@ -10,300 +10,6 @@ import ( i256 "gno.land/p/demo/int256" ) -// TODO: Set up simple tests for now and modify them later to check for more complex cases. - -func parseUintFromString(s string) (*Uint, error) { - if s == "0" { - return NewUint(0), nil - } - value, err := parseUint(s, 16, 64) - if err != nil { - return nil, err - } - return NewUint(value), nil -} - -type binOpTest struct { - x, y, want string -} - -// TODO: use `SetBytes` to test hex strings -func TestAdd(t *testing.T) { - tests := []binOpTest{ - {"0", "1", "1"}, - {"1", "0", "1"}, - {"1", "1", "2"}, - {"1", "3", "4"}, - {"10", "10", "20"}, - } - - for _, tc := range tests { - x, err := parseUintFromString(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := parseUintFromString(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := parseUintFromString(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := &Uint{} - got.Add(x, y) - - for i := range got.arr { - if got.arr[i] != want.arr[i] { - t.Errorf("Add(%s, %s) = %v, want %v", tc.x, tc.y, got, want) - break - } - } - } -} - -// TODO: need to test negative results -func TestSub(t *testing.T) { - tests := []binOpTest{ - {"1", "0", "1"}, - {"1", "1", "0"}, - {"10", "10", "0"}, - } - - for _, tc := range tests { - x, err := parseUintFromString(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := parseUintFromString(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := parseUintFromString(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := &Uint{} - got.Sub(x, y) - - for i := range got.arr { - if got.arr[i] != want.arr[i] { - t.Errorf("Sub(%s, %s) = %v, want %v", tc.x, tc.y, got, want) - break - } - } - } -} - -func TestMul(t *testing.T) { - tests := []binOpTest{ - {"1", "0", "0"}, - {"1", "1", "1"}, - {"10", "10", "100"}, - } - - for _, tc := range tests { - x, err := parseUintFromString(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := parseUintFromString(tc.y) - if err != nil { - t.Error(err) - continue - } - - want, err := parseUintFromString(tc.want) - if err != nil { - t.Error(err) - continue - } - - got := &Uint{} - got.Mul(x, y) - - for i := range got.arr { - if got.arr[i] != want.arr[i] { - t.Errorf("Mul(%s, %s) = %v, want %v", tc.x, tc.y, got, want) - break - } - } - } -} - -func TestDivMod(t *testing.T) { - tests := []struct { - x string - y string - wantDiv string - wantMod string - }{ - {"1", "1", "1", "0"}, - {"10", "10", "1", "0"}, - {"100", "10", "10", "0"}, - } - - for _, tc := range tests { - x, err := parseUintFromString(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := parseUintFromString(tc.y) - if err != nil { - t.Error(err) - continue - } - - wantDiv, err := parseUintFromString(tc.wantDiv) - if err != nil { - t.Error(err) - continue - } - - wantMod, err := parseUintFromString(tc.wantMod) - if err != nil { - t.Error(err) - continue - } - - gotDiv := new(Uint) - gotMod := new(Uint) - gotDiv.DivMod(x, y, gotMod) - - for i := range gotDiv.arr { - if gotDiv.arr[i] != wantDiv.arr[i] { - t.Errorf("DivMod(%s, %s) got Div %v, want Div %v", tc.x, tc.y, gotDiv, wantDiv) - break - } - } - for i := range gotMod.arr { - if gotMod.arr[i] != wantMod.arr[i] { - t.Errorf("DivMod(%s, %s) got Mod %v, want Mod %v", tc.x, tc.y, gotMod, wantMod) - break - } - } - } -} - -func TestIsZero(t *testing.T) { - tests := []struct { - x string - want bool - }{ - {"0", true}, - {"1", false}, - {"10", false}, - } - - for _, tc := range tests { - x, err := parseUintFromString(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 TestIsUint64(t *testing.T) { - tests := []struct { - x string - want bool - }{ - {"0", true}, - {"1", true}, - {"10", true}, - {"ffffffffffffffff", true}, - {"10000000000000000", true}, - } - - for _, tc := range tests { - x := new(Uint).SetBytes(hex2Bytes(tc.x)) - got := x.IsUint64() - - if got != tc.want { - t.Errorf("IsUint64(%s) = %v, want %v", tc.x, got, tc.want) - } - } -} - -func TestLtUint64(t *testing.T) { - tests := []struct { - x string - y uint64 - want bool - }{ - {"0", 1, true}, - {"1", 0, false}, - {"10", 10, false}, - {"ffffffffffffffff", 0, false}, - {"10000000000000000", 0, false}, - } - - for _, tc := range tests { - x := new(Uint).SetBytes(hex2Bytes(tc.x)) - got := x.LtUint64(tc.y) - - if got != tc.want { - t.Errorf("LtUint64(%s, %d) = %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", "1", 0}, - {"10", "10", 0}, - {"10", "11", -1}, - {"11", "10", 1}, - } - - for _, tc := range tests { - x, err := parseUintFromString(tc.x) - if err != nil { - t.Error(err) - continue - } - - y, err := parseUintFromString(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 hex2Bytes(s string) []byte { h, _ := hex.DecodeString(s) return h @@ -349,23 +55,3 @@ func TestByte(t *testing.T) { t.Fatalf("Expected %x, got %x", expected, actual) } } - -func TestSGT(t *testing.T) { - x := new(Uint).SetBytes(hex2Bytes("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")) - y := new(Uint).SetBytes(hex2Bytes("00")) - actual := x.Sgt(y) - if actual { - t.Fatalf("Expected %v false", actual) - } - - x = new(Uint).SetBytes(hex2Bytes("00")) - y = new(Uint).SetBytes(hex2Bytes("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")) - actual = x.Sgt(y) - if !actual { - t.Fatalf("Expected %v true", actual) - } -} - -type logicOpTest struct { - x, y, want string -} diff --git a/examples/gno.land/p/demo/uint256/utils.gno b/examples/gno.land/p/demo/uint256/utils.gno new file mode 100644 index 00000000000..5cfdbe44819 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/utils.gno @@ -0,0 +1,195 @@ +package uint256 + +import ( + "errors" +) + +// lower(c) is a lower-case letter if and only if +// c is either that lower-case letter or the equivalent upper-case letter. +// Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'. +// Note that lower of non-letters can produce other non-letters. +func lower(c byte) byte { + return c | ('x' - 'X') +} + +// underscoreOK reports whether the underscores in s are allowed. +// Checking them in this one function lets all the parsers skip over them simply. +// Underscore must appear only between digits or between a base prefix and a digit. +func underscoreOK(s string) bool { + // saw tracks the last character (class) we saw: + // ^ for beginning of number, + // 0 for a digit or base prefix, + // _ for an underscore, + // ! for none of the above. + saw := '^' + i := 0 + + // Optional sign. + if len(s) >= 1 && (s[0] == '-' || s[0] == '+') { + s = s[1:] + } + + // Optional base prefix. + hex := false + if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') { + i = 2 + saw = '0' // base prefix counts as a digit for "underscore as digit separator" + hex = lower(s[1]) == 'x' + } + + // Number proper. + for ; i < len(s); i++ { + // Digits are always okay. + if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' { + saw = '0' + continue + } + // Underscore must follow digit. + if s[i] == '_' { + if saw != '0' { + return false + } + saw = '_' + continue + } + // Underscore must also be followed by digit. + if saw == '_' { + return false + } + // Saw non-digit, non-underscore. + saw = '!' + } + return saw != '_' +} + +func checkNumberS(input string) error { + const fn = "UnmarshalText" + l := len(input) + if l == 0 { + return errEmptyString(fn, input) + } + if l < 2 || input[0] != '0' || + (input[1] != 'x' && input[1] != 'X') { + return errMissingPrefix(fn, input) + } + if l == 2 { + return errEmptyNumber(fn, input) + } + if len(input) > 3 && input[2] == '0' { + return errLeadingZero(fn, input) + } + return nil +} + +// ParseUint is like ParseUint but for unsigned numbers. +// +// A sign prefix is not permitted. +func parseUint(s string, base int, bitSize int) (uint64, error) { + const fnParseUint = "ParseUint" + + if s == "" { + return 0, errSyntax(fnParseUint, s) + } + + base0 := base == 0 + + s0 := s + switch { + case 2 <= base && base <= 36: + // valid base; nothing to do + + case base == 0: + // Look for octal, hex prefix. + base = 10 + if s[0] == '0' { + switch { + case len(s) >= 3 && lower(s[1]) == 'b': + base = 2 + s = s[2:] + case len(s) >= 3 && lower(s[1]) == 'o': + base = 8 + s = s[2:] + case len(s) >= 3 && lower(s[1]) == 'x': + base = 16 + s = s[2:] + default: + base = 8 + s = s[1:] + } + } + + default: + return 0, errInvalidBase(fnParseUint, base) + } + + if bitSize == 0 { + bitSize = uintSize + } else if bitSize < 0 || bitSize > 64 { + return 0, errInvalidBitSize(fnParseUint, bitSize) + } + + // Cutoff is the smallest number such that cutoff*base > maxUint64. + // Use compile-time constants for common cases. + var cutoff uint64 + switch base { + case 10: + cutoff = MaxUint64/10 + 1 + case 16: + cutoff = MaxUint64/16 + 1 + default: + cutoff = MaxUint64/uint64(base) + 1 + } + + maxVal := uint64(1)<= byte(base) { + return 0, errSyntax(fnParseUint, s0) + } + + if n >= cutoff { + // n*base overflows + return maxVal, errRange(fnParseUint, s0) + } + n *= uint64(base) + + n1 := n + uint64(d) + if n1 < n || n1 > maxVal { + // n+d overflows + return maxVal, errRange(fnParseUint, s0) + } + n = n1 + } + + if underscores && !underscoreOK(s0) { + return 0, errSyntax(fnParseUint, s0) + } + + return n, nil +} + +func parseUintFromString(s string) (*Uint, error) { + if s == "0" { + return NewUint(0), nil + } + value, err := parseUint(s, 16, 64) + if err != nil { + return nil, err + } + return NewUint(value), nil +} From e50816bcd18bf3fef25d9c73ffc6a2c5376d9c0a Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Tue, 19 Mar 2024 17:46:59 +0900 Subject: [PATCH 07/23] add readme --- examples/gno.land/p/demo/uint256/README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 examples/gno.land/p/demo/uint256/README.md diff --git a/examples/gno.land/p/demo/uint256/README.md b/examples/gno.land/p/demo/uint256/README.md new file mode 100644 index 00000000000..b580cd33df6 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/README.md @@ -0,0 +1,5 @@ +# Fixed size 256-bit math library + +This is a library specialized at replacing the `big.Int` library for math based on 256-bit types. + +original repository: [uint256]() From cceacfd3955c33d372e4cb202857e1d1a9a97977 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Tue, 19 Mar 2024 17:47:24 +0900 Subject: [PATCH 08/23] add some missed functions --- .../gno.land/p/demo/uint256/arithmetic.gno | 66 +++++++++++++++++++ examples/gno.land/p/demo/uint256/cmp_test.gno | 42 ++++++------ .../gno.land/p/demo/uint256/conversion.gno | 46 +++++++++++++ examples/gno.land/p/demo/uint256/uint256.gno | 19 ++++++ 4 files changed, 152 insertions(+), 21 deletions(-) diff --git a/examples/gno.land/p/demo/uint256/arithmetic.gno b/examples/gno.land/p/demo/uint256/arithmetic.gno index 2bac1ea9ca4..7538a549914 100644 --- a/examples/gno.land/p/demo/uint256/arithmetic.gno +++ b/examples/gno.land/p/demo/uint256/arithmetic.gno @@ -185,6 +185,72 @@ func (z *Uint) DivMod(x, y, m *Uint) (*Uint, *Uint) { return z, m } +// Exp sets z = base**exponent mod 2**256, and returns z. +func (z *Uint) Exp(base, exponent *Uint) *Uint { + res := Uint{arr: [4]uint64{1, 0, 0, 0}} + multiplier := *base + expBitLen := exponent.BitLen() + + curBit := 0 + word := exponent.arr[0] + for ; curBit < expBitLen && curBit < 64; curBit++ { + if word&1 == 1 { + res.Mul(&res, &multiplier) + } + multiplier.squared() + word >>= 1 + } + + word = exponent.arr[1] + for ; curBit < expBitLen && curBit < 128; curBit++ { + if word&1 == 1 { + res.Mul(&res, &multiplier) + } + multiplier.squared() + word >>= 1 + } + + word = exponent.arr[2] + for ; curBit < expBitLen && curBit < 192; curBit++ { + if word&1 == 1 { + res.Mul(&res, &multiplier) + } + multiplier.squared() + word >>= 1 + } + + word = exponent.arr[3] + for ; curBit < expBitLen && curBit < 256; curBit++ { + if word&1 == 1 { + res.Mul(&res, &multiplier) + } + multiplier.squared() + word >>= 1 + } + return z.Set(&res) +} + +func (z *Uint) squared() { + var ( + res Uint + carry0, carry1, carry2 uint64 + res1, res2 uint64 + ) + + carry0, res.arr[0] = bits.Mul64(z.arr[0], z.arr[0]) + carry0, res1 = umulHop(carry0, z.arr[0], z.arr[1]) + carry0, res2 = umulHop(carry0, z.arr[0], z.arr[2]) + + carry1, res.arr[1] = umulHop(res1, z.arr[0], z.arr[1]) + carry1, res2 = umulStep(res2, z.arr[1], z.arr[1], carry1) + + carry2, res.arr[2] = umulHop(res2, z.arr[0], z.arr[2]) + + res.arr[3] = 2*(z.arr[0]*z.arr[3]+z.arr[1]*z.arr[2]) + carry0 + carry1 + carry2 + + z.Set(&res) +} + // udivrem divides u by d and produces both quotient and remainder. // The quotient is stored in provided quot - len(u)-len(d)+1 words. // It loosely follows the Knuth's division algorithm (sometimes referenced as "schoolbook" division) using 64-bit words. diff --git a/examples/gno.land/p/demo/uint256/cmp_test.gno b/examples/gno.land/p/demo/uint256/cmp_test.gno index 8ae2f720ac2..ffa27fc8c89 100644 --- a/examples/gno.land/p/demo/uint256/cmp_test.gno +++ b/examples/gno.land/p/demo/uint256/cmp_test.gno @@ -38,49 +38,49 @@ func TestCmp(t *testing.T) { } } -func TestLtUint64(t *testing.T) { +func TestIsZero(t *testing.T) { tests := []struct { x string - y uint64 want bool }{ - {"0", 1, true}, - {"1", 0, false}, - {"10", 10, false}, - {"ffffffffffffffff", 0, false}, - {"10000000000000000", 0, false}, + {"0", true}, + {"1", false}, + {"10", false}, } for _, tc := range tests { - x := new(Uint).SetBytes(hex2Bytes(tc.x)) - got := x.LtUint64(tc.y) + x, err := parseUintFromString(tc.x) + if err != nil { + t.Error(err) + continue + } + got := x.IsZero() if got != tc.want { - t.Errorf("LtUint64(%s, %d) = %v, want %v", tc.x, tc.y, got, tc.want) + t.Errorf("IsZero(%s) = %v, want %v", tc.x, got, tc.want) } } } -func TestIsZero(t *testing.T) { +func TestLtUint64(t *testing.T) { tests := []struct { x string + y uint64 want bool }{ - {"0", true}, - {"1", false}, - {"10", false}, + {"0", 1, true}, + {"1", 0, false}, + {"10", 10, false}, + {"ffffffffffffffff", 0, false}, + {"10000000000000000", 0, false}, } for _, tc := range tests { - x, err := parseUintFromString(tc.x) - if err != nil { - t.Error(err) - continue - } + x := new(Uint).SetBytes(hex2Bytes(tc.x)) + got := x.LtUint64(tc.y) - got := x.IsZero() if got != tc.want { - t.Errorf("IsZero(%s) = %v, want %v", tc.x, got, tc.want) + t.Errorf("LtUint64(%s, %d) = %v, want %v", tc.x, tc.y, got, tc.want) } } } diff --git a/examples/gno.land/p/demo/uint256/conversion.gno b/examples/gno.land/p/demo/uint256/conversion.gno index 5576ae1635d..13308c57c43 100644 --- a/examples/gno.land/p/demo/uint256/conversion.gno +++ b/examples/gno.land/p/demo/uint256/conversion.gno @@ -5,7 +5,9 @@ package uint256 import ( "encoding/binary" + "errors" "strconv" + "strings" ) // Uint64 returns the lower 64-bits of z @@ -81,6 +83,50 @@ func (z *Uint) Dec() string { return string(out[pos-len(buf):]) } +func (z *Uint) Scan(src interface{}) error { + if src == nil { + z.Clear() + return nil + } + + switch src := src.(type) { + case string: + case []byte: + default: + return errors.New("unsupported type: can't convert to uint256.Uint") + } +} + +func (z *Uint) scanScientificFromString(src string) error { + if len(src) == 0 { + z.Clear() + return nil + } + + idx := strings.IndexByte(src, 'e') + if idx == -1 { + return z.SetFromDecimal(src) + } + if err := z.SetFromDecimal(src[:idx]); err != nil { + return err + } + if src[(idx+1):] == "0" { + return nil + } + exp := new(Uint) + if err := exp.SetFromDecimal(src[(idx+1):]); err != nil { + return err + } + if exp.GtUint64(77) { // 10**78 is larger than 2**256 + return ErrBig256Range + } + exp.Exp(NewUint(10), exp) + if _, overflow := z.MulOverflow(z, exp); overflow { + return ErrBig256Range + } + return nil +} + // ToString returns the decimal string representation of z. It returns an empty string if z is nil. func (z *Uint) ToString() string { if z == nil { diff --git a/examples/gno.land/p/demo/uint256/uint256.gno b/examples/gno.land/p/demo/uint256/uint256.gno index 8175736aa72..d20150ca653 100644 --- a/examples/gno.land/p/demo/uint256/uint256.gno +++ b/examples/gno.land/p/demo/uint256/uint256.gno @@ -175,6 +175,25 @@ func (z *Uint) Byte(n *Uint) *Uint { return z.Clear() } +// BitLen returns the number of bits required to represent z +func (z *Uint) BitLen() int { + switch { + case z.arr[3] != 0: + return 192 + bits.Len64(z.arr[3]) + case z.arr[2] != 0: + return 128 + bits.Len64(z.arr[2]) + case z.arr[1] != 0: + return 64 + bits.Len64(z.arr[1]) + default: + return bits.Len64(z.arr[0]) + } +} + +// ByteLen returns the number of bytes required to represent z +func (z *Uint) ByteLen() int { + return (z.BitLen() + 7) / 8 +} + // Clear sets z to 0 func (z *Uint) Clear() *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 0 From adf1a1055b26b4a6af117aecb664039ebd587946 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 26 Mar 2024 19:38:51 +0900 Subject: [PATCH 09/23] test: more arithmetic --- .../p/demo/uint256/arithmetic_test.gno | 220 +++++++++++++++--- examples/gno.land/p/demo/uint256/cmp.gno | 7 +- 2 files changed, 188 insertions(+), 39 deletions(-) diff --git a/examples/gno.land/p/demo/uint256/arithmetic_test.gno b/examples/gno.land/p/demo/uint256/arithmetic_test.gno index 633bdf73f14..9f45a507754 100644 --- a/examples/gno.land/p/demo/uint256/arithmetic_test.gno +++ b/examples/gno.land/p/demo/uint256/arithmetic_test.gno @@ -1,36 +1,35 @@ package uint256 -import ( - "testing" -) +import "testing" -type binOpTest struct { +type binOp2Test struct { x, y, want string } func TestAdd(t *testing.T) { - tests := []binOpTest{ + tests := []binOp2Test{ {"0", "1", "1"}, {"1", "0", "1"}, {"1", "1", "2"}, {"1", "3", "4"}, {"10", "10", "20"}, + {"18446744073709551615", "18446744073709551615", "36893488147419103230"}, // uint64 overflow } for _, tc := range tests { - x, err := parseUintFromString(tc.x) + x, err := FromDecimal(tc.x) if err != nil { t.Error(err) continue } - y, err := parseUintFromString(tc.y) + y, err := FromDecimal(tc.y) if err != nil { t.Error(err) continue } - want, err := parseUintFromString(tc.want) + want, err := FromDecimal(tc.want) if err != nil { t.Error(err) continue @@ -39,37 +38,35 @@ func TestAdd(t *testing.T) { got := &Uint{} got.Add(x, y) - for i := range got.arr { - if got.arr[i] != want.arr[i] { - t.Errorf("Add(%s, %s) = %v, want %v", tc.x, tc.y, got, want) - break - } + if got.Neq(want) { + t.Errorf("Add(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) } } } -// TODO: need to test negative results func TestSub(t *testing.T) { - tests := []binOpTest{ + tests := []binOp2Test{ {"1", "0", "1"}, {"1", "1", "0"}, {"10", "10", "0"}, + {"31337", "1337", "30000"}, + {"2", "3", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, // underflow } for _, tc := range tests { - x, err := parseUintFromString(tc.x) + x, err := FromDecimal(tc.x) if err != nil { t.Error(err) continue } - y, err := parseUintFromString(tc.y) + y, err := FromDecimal(tc.y) if err != nil { t.Error(err) continue } - want, err := parseUintFromString(tc.want) + want, err := FromDecimal(tc.want) if err != nil { t.Error(err) continue @@ -78,36 +75,34 @@ func TestSub(t *testing.T) { got := &Uint{} got.Sub(x, y) - for i := range got.arr { - if got.arr[i] != want.arr[i] { - t.Errorf("Sub(%s, %s) = %v, want %v", tc.x, tc.y, got, want) - break - } + if got.Neq(want) { + t.Errorf("Sub(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) } } } func TestMul(t *testing.T) { - tests := []binOpTest{ + tests := []binOp2Test{ {"1", "0", "0"}, {"1", "1", "1"}, {"10", "10", "100"}, + {"18446744073709551615", "2", "36893488147419103230"}, // uint64 overflow } for _, tc := range tests { - x, err := parseUintFromString(tc.x) + x, err := FromDecimal(tc.x) if err != nil { t.Error(err) continue } - y, err := parseUintFromString(tc.y) + y, err := FromDecimal(tc.y) if err != nil { t.Error(err) continue } - want, err := parseUintFromString(tc.want) + want, err := FromDecimal(tc.want) if err != nil { t.Error(err) continue @@ -116,47 +111,121 @@ func TestMul(t *testing.T) { got := &Uint{} got.Mul(x, y) - for i := range got.arr { - if got.arr[i] != want.arr[i] { - t.Errorf("Mul(%s, %s) = %v, want %v", tc.x, tc.y, got, want) - break - } + if got.Neq(want) { + t.Errorf("Mul(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestDiv(t *testing.T) { + tests := []binOp2Test{ + {"31337", "3", "10445"}, + {"31337", "0", "0"}, + {"0", "31337", "0"}, + {"1", "1", "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 := &Uint{} + 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 TestMod(t *testing.T) { + tests := []binOp2Test{ + {"31337", "3", "2"}, + {"31337", "0", "0"}, + {"0", "31337", "0"}, + {"2", "31337", "2"}, + {"1", "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 := &Uint{} + got.Mod(x, y) + + if got.Neq(want) { + t.Errorf("Mod(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) } } } func TestDivMod(t *testing.T) { tests := []struct { - x string - y string + x string + y string wantDiv string wantMod string }{ {"1", "1", "1", "0"}, {"10", "10", "1", "0"}, {"100", "10", "10", "0"}, + {"31337", "3", "10445", "2"}, + {"31337", "0", "0", "0"}, + {"0", "31337", "0", "0"}, + {"2", "31337", "0", "2"}, } for _, tc := range tests { - x, err := parseUintFromString(tc.x) + x, err := FromDecimal(tc.x) if err != nil { t.Error(err) continue } - y, err := parseUintFromString(tc.y) + y, err := FromDecimal(tc.y) if err != nil { t.Error(err) continue } - wantDiv, err := parseUintFromString(tc.wantDiv) + wantDiv, err := FromDecimal(tc.wantDiv) if err != nil { t.Error(err) continue } - wantMod, err := parseUintFromString(tc.wantMod) + wantMod, err := FromDecimal(tc.wantMod) if err != nil { t.Error(err) continue @@ -180,3 +249,78 @@ func TestDivMod(t *testing.T) { } } } + +func TestNeg(t *testing.T) { + tests := []struct { + x string + want string + }{ + {"31337", "115792089237316195423570985008687907853269984665640564039457584007913129608599"}, + {"115792089237316195423570985008687907853269984665640564039457584007913129608599", "31337"}, + {"0", "0"}, + {"2", "115792089237316195423570985008687907853269984665640564039457584007913129639934"}, + {"1", "115792089237316195423570985008687907853269984665640564039457584007913129639935"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Neg(x) + + if got.Neq(want) { + t.Errorf("Neg(%s) = %v, want %v", tc.x, got.ToString(), want.ToString()) + } + } +} + +func TestExp(t *testing.T) { + tests := []binOp2Test{ + {"31337", "3", "30773171189753"}, + {"31337", "0", "1"}, + {"0", "31337", "0"}, + {"1", "1", "1"}, + {"2", "3", "8"}, + {"2", "64", "18446744073709551616"}, + {"2", "128", "340282366920938463463374607431768211456"}, + {"2", "255", "57896044618658097711785492504343953926634992332820282019728792003956564819968"}, + {"2", "256", "0"}, // overflow + } + + 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 := &Uint{} + got.Exp(x, y) + + if got.Neq(want) { + t.Errorf("Exp(%s, %s) = %v, want %v", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} diff --git a/examples/gno.land/p/demo/uint256/cmp.gno b/examples/gno.land/p/demo/uint256/cmp.gno index c31706ac1d5..a43a31e5ff7 100644 --- a/examples/gno.land/p/demo/uint256/cmp.gno +++ b/examples/gno.land/p/demo/uint256/cmp.gno @@ -103,6 +103,11 @@ func (z *Uint) Eq(x *Uint) bool { return (z.arr[0] == x.arr[0]) && (z.arr[1] == x.arr[1]) && (z.arr[2] == x.arr[2]) && (z.arr[3] == x.arr[3]) } +// Neq returns true if z != x +func (z *Uint) Neq(x *Uint) bool { + return !z.Eq(x) +} + // Sgt interprets z and x as signed integers, and returns // true if z > x func (z *Uint) Sgt(x *Uint) bool { @@ -117,4 +122,4 @@ func (z *Uint) Sgt(x *Uint) bool { default: return z.Gt(x) } -} \ No newline at end of file +} From 4e9f558dbd3a987cdb0967bf2e483cc32e03db6d Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 26 Mar 2024 19:42:16 +0900 Subject: [PATCH 10/23] chore: tidy --- examples/gno.land/p/demo/int256/int256.gno | 1 - .../gno.land/p/demo/uint256/arithmetic.gno | 2 +- .../gno.land/p/demo/uint256/bits_table.gno | 2 +- .../gno.land/p/demo/uint256/bitwise_test.gno | 78 +++++++++---------- .../gno.land/p/demo/uint256/conversion.gno | 4 +- .../p/demo/uint256/conversion_test.gno | 58 +++++++------- examples/gno.land/p/demo/uint256/error.gno | 6 +- examples/gno.land/p/demo/uint256/mod.gno | 2 - .../gno.land/p/demo/uint256/uint256_test.gno | 4 +- 9 files changed, 77 insertions(+), 80 deletions(-) diff --git a/examples/gno.land/p/demo/int256/int256.gno b/examples/gno.land/p/demo/int256/int256.gno index 30412935184..0d67eb4e0b6 100644 --- a/examples/gno.land/p/demo/int256/int256.gno +++ b/examples/gno.land/p/demo/int256/int256.gno @@ -507,7 +507,6 @@ func (z *Int) Uint64() uint64 { // Int64 returns the lower 64-bits of z func (z *Int) Int64() int64 { - _abs := z.abs.Clone() if z.neg { diff --git a/examples/gno.land/p/demo/uint256/arithmetic.gno b/examples/gno.land/p/demo/uint256/arithmetic.gno index 7538a549914..c3e2ed83738 100644 --- a/examples/gno.land/p/demo/uint256/arithmetic.gno +++ b/examples/gno.land/p/demo/uint256/arithmetic.gno @@ -469,4 +469,4 @@ func udivrem2by1(uh, ul, d, reciprocal uint64) (quot, rem uint64) { } return qh, r -} \ No newline at end of file +} diff --git a/examples/gno.land/p/demo/uint256/bits_table.gno b/examples/gno.land/p/demo/uint256/bits_table.gno index 7cb2081fd19..53dbea94827 100644 --- a/examples/gno.land/p/demo/uint256/bits_table.gno +++ b/examples/gno.land/p/demo/uint256/bits_table.gno @@ -76,4 +76,4 @@ const len8tab = "" + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + - "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" \ No newline at end of file + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" diff --git a/examples/gno.land/p/demo/uint256/bitwise_test.gno b/examples/gno.land/p/demo/uint256/bitwise_test.gno index d03fd03dfd5..2fbee5cdf4a 100644 --- a/examples/gno.land/p/demo/uint256/bitwise_test.gno +++ b/examples/gno.land/p/demo/uint256/bitwise_test.gno @@ -9,25 +9,25 @@ type logicOpTest struct { } func TestAndBitForm(t *testing.T) { - testCases := []struct { - name string - x Uint - y Uint - want Uint - }{ - { - name: "all zeros", - x: Uint{arr: [4]uint64{0, 0, 0, 0}}, - y: Uint{arr: [4]uint64{0, 0, 0, 0}}, - want: Uint{arr: [4]uint64{0, 0, 0, 0}}, - }, - { - name: "all ones", - x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, - y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, - want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, - }, - { + testCases := []struct { + name string + x Uint + y Uint + want Uint + }{ + { + name: "all zeros", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { name: "mixed", x: Uint{arr: [4]uint64{0, 0, 0, 0}}, y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, @@ -35,30 +35,30 @@ func TestAndBitForm(t *testing.T) { }, { name: "mixed 2", - x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, y: Uint{arr: [4]uint64{0, 0, 0, 0}}, want: Uint{arr: [4]uint64{0, 0, 0, 0}}, }, { - name: "one operand zero", - x: Uint{arr: [4]uint64{0, 0, 0, 0}}, - y: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}}, - want: Uint{arr: [4]uint64{0, 0, 0, 0}}, - }, - { - name: "one operand all ones", - x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, - y: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, - want: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, - }, - } + name: "one operand zero", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "one operand all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + want: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + }, + } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { res := new(Uint).And(&tc.x, &tc.y) - if *res != tc.want { - t.Errorf("And(%v, %v) = %v, want %v", tc.x, tc.y, *res, tc.want) - } - }) - } + if *res != tc.want { + t.Errorf("And(%v, %v) = %v, want %v", tc.x, tc.y, *res, tc.want) + } + }) + } } diff --git a/examples/gno.land/p/demo/uint256/conversion.gno b/examples/gno.land/p/demo/uint256/conversion.gno index 13308c57c43..eb8d61d9226 100644 --- a/examples/gno.land/p/demo/uint256/conversion.gno +++ b/examples/gno.land/p/demo/uint256/conversion.gno @@ -114,7 +114,7 @@ func (z *Uint) scanScientificFromString(src string) error { return nil } exp := new(Uint) - if err := exp.SetFromDecimal(src[(idx+1):]); err != nil { + if err := exp.SetFromDecimal(src[(idx + 1):]); err != nil { return err } if exp.GtUint64(77) { // 10**78 is larger than 2**256 @@ -565,4 +565,4 @@ func bigEndianUint48(b []byte) uint64 { _ = b[5] // bounds check hint to compiler; see golang.org/issue/14808 return uint64(b[5]) | uint64(b[4])<<8 | uint64(b[3])<<16 | uint64(b[2])<<24 | uint64(b[1])<<32 | uint64(b[0])<<40 -} \ No newline at end of file +} diff --git a/examples/gno.land/p/demo/uint256/conversion_test.gno b/examples/gno.land/p/demo/uint256/conversion_test.gno index 7969e8c8d94..ee314f00e5c 100644 --- a/examples/gno.land/p/demo/uint256/conversion_test.gno +++ b/examples/gno.land/p/demo/uint256/conversion_test.gno @@ -27,34 +27,34 @@ func TestIsUint64(t *testing.T) { } func TestDec(t *testing.T) { - testCases := []struct { - name string - z Uint - want string - }{ - { - name: "zero", - z: Uint{arr: [4]uint64{0, 0, 0, 0}}, - want: "0", - }, - { - name: "less than 20 digits", - z: Uint{arr: [4]uint64{1234567890, 0, 0, 0}}, - want: "1234567890", - }, - { - name: "max possible value", - z: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, - want: "115792089237316195423570985008687907853269984665640564039457584007913129639935", - }, - } + testCases := []struct { + name string + z Uint + want string + }{ + { + name: "zero", + z: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: "0", + }, + { + name: "less than 20 digits", + z: Uint{arr: [4]uint64{1234567890, 0, 0, 0}}, + want: "1234567890", + }, + { + name: "max possible value", + z: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: "115792089237316195423570985008687907853269984665640564039457584007913129639935", + }, + } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - result := tc.z.Dec() - if result != tc.want { - t.Errorf("Dec(%v) = %s, want %s", tc.z, result, tc.want) - } - }) - } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := tc.z.Dec() + if result != tc.want { + t.Errorf("Dec(%v) = %s, want %s", tc.z, result, tc.want) + } + }) + } } diff --git a/examples/gno.land/p/demo/uint256/error.gno b/examples/gno.land/p/demo/uint256/error.gno index ef7363ff3e4..d200bb9cc8f 100644 --- a/examples/gno.land/p/demo/uint256/error.gno +++ b/examples/gno.land/p/demo/uint256/error.gno @@ -19,9 +19,9 @@ var ( ) type u256Error struct { - fn string // function name + fn string // function name input string - err error + err error } func (e *u256Error) Error() string { @@ -70,4 +70,4 @@ func errInvalidBase(fn string, base int) error { func errInvalidBitSize(fn string, bitSize int) error { return &u256Error{fn: fn, input: string(bitSize), err: ErrInvalidBitSize} -} \ No newline at end of file +} diff --git a/examples/gno.land/p/demo/uint256/mod.gno b/examples/gno.land/p/demo/uint256/mod.gno index 16f312ea3b2..40d5b2d6246 100644 --- a/examples/gno.land/p/demo/uint256/mod.gno +++ b/examples/gno.land/p/demo/uint256/mod.gno @@ -35,7 +35,6 @@ func leadingZeros(x *Uint) (z int) { // - returns zero if m.arr[3] == 0 // - starts with a 32-bit division, refines with newton-raphson iterations func Reciprocal(m *Uint) (mu [5]uint64) { - if m.arr[3] == 0 { return mu } @@ -443,7 +442,6 @@ func Reciprocal(m *Uint) (mu [5]uint64) { // // requires a four-word modulus (m.arr[3] > 1) and its inverse (mu) func reduce4(x [8]uint64, m *Uint, mu [5]uint64) (z Uint) { - // NB: Most variable names in the comments match the pseudocode for // Barrett reduction in the Handbook of Applied Cryptography. diff --git a/examples/gno.land/p/demo/uint256/uint256_test.gno b/examples/gno.land/p/demo/uint256/uint256_test.gno index 04ca85e66ea..18ccd73a13e 100644 --- a/examples/gno.land/p/demo/uint256/uint256_test.gno +++ b/examples/gno.land/p/demo/uint256/uint256_test.gno @@ -6,8 +6,8 @@ import ( "strings" "testing" - "gno.land/p/demo/ufmt" i256 "gno.land/p/demo/int256" + "gno.land/p/demo/ufmt" ) func hex2Bytes(s string) []byte { @@ -43,7 +43,7 @@ func TestByte(t *testing.T) { z = new(Uint).SetBytes(hex2Bytes("ABCDEF09080706050403020100000000000000000000000000000000000000ef")) actual = z.Byte(NewUint(32)) - expected =new(Uint).SetBytes(hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) + expected = new(Uint).SetBytes(hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) if !actual.Eq(expected) { t.Fatalf("Expected %x, got %x", expected, actual) } From 0f2cc25e6418d62de2f7143d364db172945f2840 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 26 Mar 2024 20:27:27 +0900 Subject: [PATCH 11/23] test: more bitwise --- examples/gno.land/p/demo/uint256/bitwise.gno | 1 + .../gno.land/p/demo/uint256/bitwise_test.gno | 302 +++++++++++++++++- 2 files changed, 293 insertions(+), 10 deletions(-) diff --git a/examples/gno.land/p/demo/uint256/bitwise.gno b/examples/gno.land/p/demo/uint256/bitwise.gno index 90905e2f48f..fc6e098dc91 100644 --- a/examples/gno.land/p/demo/uint256/bitwise.gno +++ b/examples/gno.land/p/demo/uint256/bitwise.gno @@ -27,6 +27,7 @@ func (z *Uint) Not(x *Uint) *Uint { return z } +// AndNot sets z = x &^ y and returns z. func (z *Uint) AndNot(x, y *Uint) *Uint { z.arr[0] = x.arr[0] &^ y.arr[0] z.arr[1] = x.arr[1] &^ y.arr[1] diff --git a/examples/gno.land/p/demo/uint256/bitwise_test.gno b/examples/gno.land/p/demo/uint256/bitwise_test.gno index 2fbee5cdf4a..4437e7d5c50 100644 --- a/examples/gno.land/p/demo/uint256/bitwise_test.gno +++ b/examples/gno.land/p/demo/uint256/bitwise_test.gno @@ -5,16 +5,52 @@ import ( ) type logicOpTest struct { - x, y, want string + name string + x Uint + y Uint + want Uint } -func TestAndBitForm(t *testing.T) { - testCases := []struct { - name string - x Uint - y Uint - want Uint - }{ +func TestOr(t *testing.T) { + tests := []logicOpTest{ + { + name: "all zeros", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "mixed", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "one operand all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + res := new(Uint).Or(&tc.x, &tc.y) + if *res != tc.want { + t.Errorf("Or(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) + } + }) + } +} + +func TestAnd(t *testing.T) { + tests := []logicOpTest{ { name: "all zeros", x: Uint{arr: [4]uint64{0, 0, 0, 0}}, @@ -39,6 +75,12 @@ func TestAndBitForm(t *testing.T) { y: Uint{arr: [4]uint64{0, 0, 0, 0}}, want: Uint{arr: [4]uint64{0, 0, 0, 0}}, }, + { + name: "mixed 3", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, { name: "one operand zero", x: Uint{arr: [4]uint64{0, 0, 0, 0}}, @@ -53,12 +95,252 @@ func TestAndBitForm(t *testing.T) { }, } - for _, tc := range testCases { + for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { res := new(Uint).And(&tc.x, &tc.y) if *res != tc.want { - t.Errorf("And(%v, %v) = %v, want %v", tc.x, tc.y, *res, tc.want) + t.Errorf("And(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) + } + }) + } +} + +func TestNot(t *testing.T) { + tests := []struct { + name string + x Uint + want Uint + }{ + { + name: "all zeros", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "mixed", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + res := new(Uint).Not(&tc.x) + if *res != tc.want { + t.Errorf("Not(%s) = %s, want %s", tc.x.ToString(), res.ToString(), (tc.want).ToString()) + } + }) + } +} + +func TestAndNot(t *testing.T) { + tests := []logicOpTest{ + { + name: "all zeros", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "mixed", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "mixed 2", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "mixed 3", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + }, + { + name: "one operand zero", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "one operand all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + want: Uint{arr: [4]uint64{0xAAAAAAAAAAAAAAAA, 0x5555555555555555, 0x0000000000000000, ^uint64(0)}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + res := new(Uint).AndNot(&tc.x, &tc.y) + if *res != tc.want { + t.Errorf("AndNot(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) } }) } } + +func TestXor(t *testing.T) { + tests := []logicOpTest{ + { + name: "all zeros", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{0, 0, 0, 0}}, + }, + { + name: "mixed", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "mixed 2", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0, 0, 0, 0}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "mixed 3", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), 0, 0}}, + y: Uint{arr: [4]uint64{0, 0, ^uint64(0), ^uint64(0)}}, + want: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + }, + { + name: "one operand zero", + x: Uint{arr: [4]uint64{0, 0, 0, 0}}, + y: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}}, + want: Uint{arr: [4]uint64{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}}, + }, + { + name: "one operand all ones", + x: Uint{arr: [4]uint64{^uint64(0), ^uint64(0), ^uint64(0), ^uint64(0)}}, + y: Uint{arr: [4]uint64{0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000}}, + want: Uint{arr: [4]uint64{0xAAAAAAAAAAAAAAAA, 0x5555555555555555, 0x0000000000000000, ^uint64(0)}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + res := new(Uint).Xor(&tc.x, &tc.y) + if *res != tc.want { + t.Errorf("Xor(%s, %s) = %s, want %s", tc.x.ToString(), tc.y.ToString(), res.ToString(), (tc.want).ToString()) + } + }) + } +} + +func TestLsh(t *testing.T) { + tests := []struct { + x string + y uint + want string + }{ + {"0", 0, "0"}, + {"0", 1, "0"}, + {"0", 64, "0"}, + {"1", 0, "1"}, + {"1", 1, "2"}, + {"1", 64, "18446744073709551616"}, + {"1", 128, "340282366920938463463374607431768211456"}, + {"1", 192, "6277101735386680763835789423207666416102355444464034512896"}, + {"1", 255, "57896044618658097711785492504343953926634992332820282019728792003956564819968"}, + {"1", 256, "0"}, + {"31337", 0, "31337"}, + {"31337", 1, "62674"}, + {"31337", 64, "578065619037836218990592"}, + {"31337", 128, "10663428532201448629551770073089320442396672"}, + {"31337", 192, "196705537081812415096322133155058642481399512563169449530621952"}, + {"31337", 193, "393411074163624830192644266310117284962799025126338899061243904"}, + {"31337", 255, "57896044618658097711785492504343953926634992332820282019728792003956564819968"}, + {"31337", 256, "0"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Lsh(x, tc.y) + + if got.Neq(want) { + t.Errorf("Lsh(%s, %d) = %s, want %s", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} + +func TestRsh(t *testing.T) { + tests := []struct { + x string + y uint + want string + }{ + {"0", 0, "0"}, + {"0", 1, "0"}, + {"0", 64, "0"}, + {"1", 0, "1"}, + {"1", 1, "0"}, + {"1", 64, "0"}, + {"1", 128, "0"}, + {"1", 192, "0"}, + {"1", 255, "0"}, + {"57896044618658097711785492504343953926634992332820282019728792003956564819968", 255, "1"}, + {"6277101735386680763835789423207666416102355444464034512896", 192, "1"}, + {"340282366920938463463374607431768211456", 128, "1"}, + {"18446744073709551616", 64, "1"}, + {"393411074163624830192644266310117284962799025126338899061243904", 193, "31337"}, + {"196705537081812415096322133155058642481399512563169449530621952", 192, "31337"}, + {"10663428532201448629551770073089320442396672", 128, "31337"}, + {"578065619037836218990592", 64, "31337"}, + } + + for _, tc := range tests { + x, err := FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + + want, err := FromDecimal(tc.want) + if err != nil { + t.Error(err) + continue + } + + got := &Uint{} + got.Rsh(x, tc.y) + + if got.Neq(want) { + t.Errorf("Rsh(%s, %d) = %s, want %s", tc.x, tc.y, got.ToString(), want.ToString()) + } + } +} From 7774245a6b729c5241a36f8a10a67f4f0f09dc5c Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 26 Mar 2024 22:06:57 +0900 Subject: [PATCH 12/23] feat: FromHex --- examples/gno.land/p/demo/uint256/uint256.gno | 37 ++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/examples/gno.land/p/demo/uint256/uint256.gno b/examples/gno.land/p/demo/uint256/uint256.gno index d20150ca653..e3ca4a3d89b 100644 --- a/examples/gno.land/p/demo/uint256/uint256.gno +++ b/examples/gno.land/p/demo/uint256/uint256.gno @@ -4,7 +4,6 @@ package uint256 import ( "errors" "math/bits" - "strconv" ) const ( @@ -206,6 +205,18 @@ const ( badNibble = 0xff ) +// SetFromHex sets z from the given string, interpreted as a hexadecimal number. +// OBS! This method is _not_ strictly identical to the (*big.Int).SetString(..., 16) method. +// Notable differences: +// - This method _require_ "0x" or "0X" prefix. +// - This method does not accept zero-prefixed hex, e.g. "0x0001" +// - This method does not accept underscore input, e.g. "100_000", +// - This method does not accept negative zero as valid, e.g "-0x0", +// - (this method does not accept any negative input as valid) +func (z *Uint) SetFromHex(hex string) error { + return z.fromHex(hex) +} + // fromHex is the internal implementation of parsing a hex-string. func (z *Uint) fromHex(hex string) error { if err := checkNumberS(hex); err != nil { @@ -234,7 +245,29 @@ func (z *Uint) fromHex(hex string) error { return nil } -// Clone creates a new Int identical to z +// FromHex is a convenience-constructor to create an Uint from +// a hexadecimal string. The string is required to be '0x'-prefixed +// Numbers larger than 256 bits are not accepted. +func FromHex(hex string) (*Uint, error) { + var z Uint + if err := z.fromHex(hex); err != nil { + return nil, err + } + return &z, nil +} + +// MustFromHex is a convenience-constructor to create an Uint from +// a hexadecimal string. +// Returns a new Uint and panics if any error occurred. +func MustFromHex(hex string) *Uint { + var z Uint + if err := z.fromHex(hex); err != nil { + panic(err) + } + return &z +} + +// Clone creates a new Uint identical to z func (z *Uint) Clone() *Uint { var x Uint x.arr[0] = z.arr[0] From 76fc89bbd21837eb8f0cd5f77bcd48414feebba0 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 26 Mar 2024 22:07:17 +0900 Subject: [PATCH 13/23] test: more cmp --- examples/gno.land/p/demo/uint256/cmp_test.gno | 73 +++++++++++++++++-- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/p/demo/uint256/cmp_test.gno b/examples/gno.land/p/demo/uint256/cmp_test.gno index ffa27fc8c89..7d239c972b1 100644 --- a/examples/gno.land/p/demo/uint256/cmp_test.gno +++ b/examples/gno.land/p/demo/uint256/cmp_test.gno @@ -1,6 +1,7 @@ package uint256 import ( + "strings" "testing" ) @@ -19,13 +20,13 @@ func TestCmp(t *testing.T) { } for _, tc := range tests { - x, err := parseUintFromString(tc.x) + x, err := FromDecimal(tc.x) if err != nil { t.Error(err) continue } - y, err := parseUintFromString(tc.y) + y, err := FromDecimal(tc.y) if err != nil { t.Error(err) continue @@ -49,7 +50,7 @@ func TestIsZero(t *testing.T) { } for _, tc := range tests { - x, err := parseUintFromString(tc.x) + x, err := FromDecimal(tc.x) if err != nil { t.Error(err) continue @@ -71,12 +72,28 @@ func TestLtUint64(t *testing.T) { {"0", 1, true}, {"1", 0, false}, {"10", 10, false}, - {"ffffffffffffffff", 0, false}, - {"10000000000000000", 0, false}, + {"0xffffffffffffffff", 0, false}, + {"0x10000000000000000", 10000000000000000, false}, } for _, tc := range tests { - x := new(Uint).SetBytes(hex2Bytes(tc.x)) + var x *Uint + var err error + + if strings.HasPrefix(tc.x, "0x") { + x, err = FromHex(tc.x) + if err != nil { + t.Error(err) + continue + } + } else { + x, err = FromDecimal(tc.x) + if err != nil { + t.Error(err) + continue + } + } + got := x.LtUint64(tc.y) if got != tc.want { @@ -100,3 +117,47 @@ func TestSGT(t *testing.T) { t.Fatalf("Expected %v true", actual) } } + +func TestEq(t *testing.T) { + tests := []struct { + x string + y string + want bool + }{ + {"0xffffffffffffffff", "18446744073709551615", true}, + {"0x10000000000000000", "18446744073709551616", true}, + {"0", "0", true}, + {"115792089237316195423570985008687907853269984665640564039457584007913129639935", "115792089237316195423570985008687907853269984665640564039457584007913129639935", true}, + } + + for i, tc := range tests { + var x *Uint + var err error + + if strings.HasPrefix(tc.x, "0x") { + x, err = FromHex(tc.x) + if err != nil { + t.Error(err) + continue + } + } else { + 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) + } + } +} From 8330d0d931fc7122900545d9eae8a3ae827d26c8 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 26 Mar 2024 22:13:51 +0900 Subject: [PATCH 14/23] docs: conversion --- examples/gno.land/p/demo/uint256/conversion.gno | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/gno.land/p/demo/uint256/conversion.gno b/examples/gno.land/p/demo/uint256/conversion.gno index eb8d61d9226..d893ce907a4 100644 --- a/examples/gno.land/p/demo/uint256/conversion.gno +++ b/examples/gno.land/p/demo/uint256/conversion.gno @@ -128,6 +128,7 @@ func (z *Uint) scanScientificFromString(src string) error { } // ToString returns the decimal string representation of z. It returns an empty string if z is nil. +// OBS: doesn't exist from holiman's uint256 func (z *Uint) ToString() string { if z == nil { return "" From 9403d9a625eeee3540f18264d022e6bdb0bb6098 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 26 Mar 2024 22:25:19 +0900 Subject: [PATCH 15/23] feat: MustFromDecimal --- examples/gno.land/p/demo/uint256/uint256.gno | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/examples/gno.land/p/demo/uint256/uint256.gno b/examples/gno.land/p/demo/uint256/uint256.gno index e3ca4a3d89b..60edb0ba429 100644 --- a/examples/gno.land/p/demo/uint256/uint256.gno +++ b/examples/gno.land/p/demo/uint256/uint256.gno @@ -1,4 +1,5 @@ // Ported from https://github.com/holiman/uint256 +// This package provides a 256-bit unsigned integer type, Uint256, and associated functions. package uint256 import ( @@ -98,6 +99,17 @@ func FromDecimal(decimal string) (*Uint, error) { return &z, nil } +// MustFromDecimal is a convenience-constructor to create an Uint from a +// decimal (base 10) string. +// Returns a new Uint and panics if any error occurred. +func MustFromDecimal(decimal string) *Uint { + var z Uint + if err := z.SetFromDecimal(decimal); err != nil { + panic(err) + } + return &z +} + // multipliers holds the values that are needed for fromDecimal var multipliers = [5]*Uint{ nil, // represents first round, no multiplication needed From cafab138faffb5336ffe7ed68916f4e9456c1a90 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 26 Mar 2024 22:25:34 +0900 Subject: [PATCH 16/23] test: fix some tests --- examples/gno.land/p/demo/uint256/cmp_test.gno | 8 ++++---- examples/gno.land/p/demo/uint256/conversion_test.gno | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/gno.land/p/demo/uint256/cmp_test.gno b/examples/gno.land/p/demo/uint256/cmp_test.gno index 7d239c972b1..930079f70f0 100644 --- a/examples/gno.land/p/demo/uint256/cmp_test.gno +++ b/examples/gno.land/p/demo/uint256/cmp_test.gno @@ -103,15 +103,15 @@ func TestLtUint64(t *testing.T) { } func TestSGT(t *testing.T) { - x := new(Uint).SetBytes(hex2Bytes("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")) - y := new(Uint).SetBytes(hex2Bytes("00")) + x := MustFromHex("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe") + y := MustFromHex("0x0") actual := x.Sgt(y) if actual { t.Fatalf("Expected %v false", actual) } - x = new(Uint).SetBytes(hex2Bytes("00")) - y = new(Uint).SetBytes(hex2Bytes("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe")) + x = MustFromHex("0x0") + y = MustFromHex("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe") actual = x.Sgt(y) if !actual { t.Fatalf("Expected %v true", actual) diff --git a/examples/gno.land/p/demo/uint256/conversion_test.gno b/examples/gno.land/p/demo/uint256/conversion_test.gno index ee314f00e5c..12ae99cc0e9 100644 --- a/examples/gno.land/p/demo/uint256/conversion_test.gno +++ b/examples/gno.land/p/demo/uint256/conversion_test.gno @@ -9,15 +9,15 @@ func TestIsUint64(t *testing.T) { x string want bool }{ - {"0", true}, - {"1", true}, - {"10", true}, - {"ffffffffffffffff", true}, - {"10000000000000000", true}, + {"0x0", true}, + {"0x1", true}, + {"0x10", true}, + {"0xffffffffffffffff", true}, + {"0x10000000000000000", false}, } for _, tc := range tests { - x := new(Uint).SetBytes(hex2Bytes(tc.x)) + x := MustFromHex(tc.x) got := x.IsUint64() if got != tc.want { From c5aa56c4db10278e11af925e45b6eccbb99f4e2d Mon Sep 17 00:00:00 2001 From: n3wbie Date: Tue, 26 Mar 2024 22:26:28 +0900 Subject: [PATCH 17/23] chore: remove unnecessary --- examples/gno.land/p/demo/uint256/uint256.gno | 12 ---- .../gno.land/p/demo/uint256/uint256_test.gno | 56 ------------------- 2 files changed, 68 deletions(-) diff --git a/examples/gno.land/p/demo/uint256/uint256.gno b/examples/gno.land/p/demo/uint256/uint256.gno index 60edb0ba429..80da0ba882b 100644 --- a/examples/gno.land/p/demo/uint256/uint256.gno +++ b/examples/gno.land/p/demo/uint256/uint256.gno @@ -289,15 +289,3 @@ func (z *Uint) Clone() *Uint { return &x } - -func (z *Uint) IsNil() bool { - return z == nil -} - -func (z *Uint) NilToZero() *Uint { - if z == nil { - z = NewUint(0) - } - - return z -} diff --git a/examples/gno.land/p/demo/uint256/uint256_test.gno b/examples/gno.land/p/demo/uint256/uint256_test.gno index 18ccd73a13e..bd730accda3 100644 --- a/examples/gno.land/p/demo/uint256/uint256_test.gno +++ b/examples/gno.land/p/demo/uint256/uint256_test.gno @@ -1,57 +1 @@ package uint256 - -import ( - "bytes" - "encoding/hex" - "strings" - "testing" - - i256 "gno.land/p/demo/int256" - "gno.land/p/demo/ufmt" -) - -func hex2Bytes(s string) []byte { - h, _ := hex.DecodeString(s) - return h -} - -func checkOverflow(f *Uint, overflow bool) error { - hb := hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") - max := NewUint(0).SetBytes(hb) - shouldOverflow := f.Cmp(max) > 0 - if overflow != shouldOverflow { - return ufmt.Errorf("overflow should be %v, but got %v", overflow, shouldOverflow) - } - - return nil -} - -func TestByte(t *testing.T) { - z := new(Uint).SetBytes(hex2Bytes("ABCDEF09080706050403020100000000000000000000000000000000000000ef")) - actual := z.Byte(NewUint(0)) - expected := new(Uint).SetBytes(hex2Bytes("00000000000000000000000000000000000000000000000000000000000000ab")) - if !actual.Eq(expected) { - t.Fatalf("Expected %x, got %x", expected, actual) - } - - z = new(Uint).SetBytes(hex2Bytes("ABCDEF09080706050403020100000000000000000000000000000000000000ef")) - actual = z.Byte(NewUint(31)) - expected = new(Uint).SetBytes(hex2Bytes("00000000000000000000000000000000000000000000000000000000000000ef")) - if !actual.Eq(expected) { - t.Fatalf("Expected %x, got %x", expected, actual) - } - - z = new(Uint).SetBytes(hex2Bytes("ABCDEF09080706050403020100000000000000000000000000000000000000ef")) - actual = z.Byte(NewUint(32)) - expected = new(Uint).SetBytes(hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) - if !actual.Eq(expected) { - t.Fatalf("Expected %x, got %x", expected, actual) - } - - z = new(Uint).SetBytes(hex2Bytes("ABCDEF0908070605040302011111111111111111111111111111111111111111")) - actual = z.Byte(new(Uint).SetBytes(hex2Bytes("f000000000000000000000000000000000000000000000000000000000000001"))) - expected = new(Uint).SetBytes(hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) - if !actual.Eq(expected) { - t.Fatalf("Expected %x, got %x", expected, actual) - } -} From d33e0eab72897cbe8d2ed9e13f3415353862a9c2 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 27 Mar 2024 14:30:53 +0900 Subject: [PATCH 18/23] chore --- examples/gno.land/p/demo/uint256/conversion.gno | 5 +++-- examples/gno.land/p/demo/uint256/mod.gno | 4 ---- examples/gno.land/p/demo/uint256/utils.gno | 4 ---- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/examples/gno.land/p/demo/uint256/conversion.gno b/examples/gno.land/p/demo/uint256/conversion.gno index d893ce907a4..4ef90602ab3 100644 --- a/examples/gno.land/p/demo/uint256/conversion.gno +++ b/examples/gno.land/p/demo/uint256/conversion.gno @@ -91,10 +91,11 @@ func (z *Uint) Scan(src interface{}) error { switch src := src.(type) { case string: + return z.scanScientificFromString(src) case []byte: - default: - return errors.New("unsupported type: can't convert to uint256.Uint") + return z.scanScientificFromString(string(src)) } + return errors.New("default // unsupported type: can't convert to uint256.Uint") } func (z *Uint) scanScientificFromString(src string) error { diff --git a/examples/gno.land/p/demo/uint256/mod.gno b/examples/gno.land/p/demo/uint256/mod.gno index 40d5b2d6246..8fa101f9dde 100644 --- a/examples/gno.land/p/demo/uint256/mod.gno +++ b/examples/gno.land/p/demo/uint256/mod.gno @@ -1,7 +1,3 @@ -// uint256: Fixed size 256-bit math library -// Copyright 2021 uint256 Authors -// SPDX-License-Identifier: BSD-3-Clause - package uint256 import ( diff --git a/examples/gno.land/p/demo/uint256/utils.gno b/examples/gno.land/p/demo/uint256/utils.gno index 5cfdbe44819..e013696ab8f 100644 --- a/examples/gno.land/p/demo/uint256/utils.gno +++ b/examples/gno.land/p/demo/uint256/utils.gno @@ -1,9 +1,5 @@ package uint256 -import ( - "errors" -) - // lower(c) is a lower-case letter if and only if // c is either that lower-case letter or the equivalent upper-case letter. // Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'. From d1365395fa11cbc0962032a0c6d622559e72e8b8 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 27 Mar 2024 14:31:55 +0900 Subject: [PATCH 19/23] bench: print-runtime-metrics --- .../p/demo/uint256/runtime-metrics.txt | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 examples/gno.land/p/demo/uint256/runtime-metrics.txt diff --git a/examples/gno.land/p/demo/uint256/runtime-metrics.txt b/examples/gno.land/p/demo/uint256/runtime-metrics.txt new file mode 100644 index 00000000000..bba96334f98 --- /dev/null +++ b/examples/gno.land/p/demo/uint256/runtime-metrics.txt @@ -0,0 +1,130 @@ +// gno test -v=true -print-runtime-metrics=true ./ +=== RUN TestAdd +--- PASS: TestAdd (0.00s) +--- runtime: cycle=25.9k imports=3 allocs=756.0k(0.15%) +=== RUN TestSub +--- PASS: TestSub (0.00s) +--- runtime: cycle=54.7k imports=3 allocs=1.5M(0.30%) +=== RUN TestMul +--- PASS: TestMul (0.00s) +--- runtime: cycle=76.5k imports=3 allocs=2.1M(0.42%) +=== RUN TestDiv +--- PASS: TestDiv (0.00s) +--- runtime: cycle=87.6k imports=3 allocs=2.5M(0.50%) +=== RUN TestMod +--- PASS: TestMod (0.00s) +--- runtime: cycle=100.8k imports=3 allocs=3.0M(0.59%) +=== RUN TestDivMod +--- PASS: TestDivMod (0.00s) +--- runtime: cycle=128.8k imports=3 allocs=4.0M(0.80%) +=== RUN TestNeg +--- PASS: TestNeg (0.00s) +--- runtime: cycle=203.1k imports=3 allocs=5.5M(1.10%) +=== RUN TestExp +--- PASS: TestExp (0.00s) +--- runtime: cycle=356.9k imports=3 allocs=8.7M(1.73%) +=== RUN TestOr +=== RUN TestOr/all_zeros +--- PASS: TestOr/all_zeros (0.00s) +=== RUN TestOr/all_ones +--- PASS: TestOr/all_ones (0.00s) +=== RUN TestOr/mixed +--- PASS: TestOr/mixed (0.00s) +=== RUN TestOr/one_operand_all_ones +--- PASS: TestOr/one_operand_all_ones (0.00s) +--- PASS: TestOr (0.00s) +--- runtime: cycle=364.2k imports=3 allocs=9.0M(1.80%) +=== RUN TestAnd +=== RUN TestAnd/all_zeros +--- PASS: TestAnd/all_zeros (0.00s) +=== RUN TestAnd/all_ones +--- PASS: TestAnd/all_ones (0.00s) +=== RUN TestAnd/mixed +--- PASS: TestAnd/mixed (0.00s) +=== RUN TestAnd/mixed_2 +--- PASS: TestAnd/mixed_2 (0.00s) +=== RUN TestAnd/mixed_3 +--- PASS: TestAnd/mixed_3 (0.00s) +=== RUN TestAnd/one_operand_zero +--- PASS: TestAnd/one_operand_zero (0.00s) +=== RUN TestAnd/one_operand_all_ones +--- PASS: TestAnd/one_operand_all_ones (0.00s) +--- PASS: TestAnd (0.00s) +--- runtime: cycle=376.6k imports=3 allocs=9.6M(1.92%) +=== RUN TestNot +=== RUN TestNot/all_zeros +--- PASS: TestNot/all_zeros (0.00s) +=== RUN TestNot/all_ones +--- PASS: TestNot/all_ones (0.00s) +=== RUN TestNot/mixed +--- PASS: TestNot/mixed (0.00s) +--- PASS: TestNot (0.00s) +--- runtime: cycle=381.2k imports=3 allocs=9.8M(1.97%) +=== RUN TestAndNot +=== RUN TestAndNot/all_zeros +--- PASS: TestAndNot/all_zeros (0.00s) +=== RUN TestAndNot/all_ones +--- PASS: TestAndNot/all_ones (0.00s) +=== RUN TestAndNot/mixed +--- PASS: TestAndNot/mixed (0.00s) +=== RUN TestAndNot/mixed_2 +--- PASS: TestAndNot/mixed_2 (0.00s) +=== RUN TestAndNot/mixed_3 +--- PASS: TestAndNot/mixed_3 (0.00s) +=== RUN TestAndNot/one_operand_zero +--- PASS: TestAndNot/one_operand_zero (0.00s) +=== RUN TestAndNot/one_operand_all_ones +--- PASS: TestAndNot/one_operand_all_ones (0.00s) +--- PASS: TestAndNot (0.00s) +--- runtime: cycle=393.6k imports=3 allocs=10.4M(2.08%) +=== RUN TestXor +=== RUN TestXor/all_zeros +--- PASS: TestXor/all_zeros (0.00s) +=== RUN TestXor/all_ones +--- PASS: TestXor/all_ones (0.00s) +=== RUN TestXor/mixed +--- PASS: TestXor/mixed (0.00s) +=== RUN TestXor/mixed_2 +--- PASS: TestXor/mixed_2 (0.00s) +=== RUN TestXor/mixed_3 +--- PASS: TestXor/mixed_3 (0.00s) +=== RUN TestXor/one_operand_zero +--- PASS: TestXor/one_operand_zero (0.00s) +=== RUN TestXor/one_operand_all_ones +--- PASS: TestXor/one_operand_all_ones (0.00s) +--- PASS: TestXor (0.00s) +--- runtime: cycle=406.0k imports=3 allocs=11.0M(2.20%) +=== RUN TestLsh +--- PASS: TestLsh (0.00s) +--- runtime: cycle=531.2k imports=3 allocs=13.8M(2.77%) +=== RUN TestRsh +--- PASS: TestRsh (0.00s) +--- runtime: cycle=636.1k imports=3 allocs=16.3M(3.25%) +=== RUN TestCmp +--- PASS: TestCmp (0.00s) +--- runtime: cycle=647.6k imports=3 allocs=16.7M(3.34%) +=== RUN TestIsZero +--- PASS: TestIsZero (0.00s) +--- runtime: cycle=650.3k imports=3 allocs=16.8M(3.36%) +=== RUN TestLtUint64 +--- PASS: TestLtUint64 (0.00s) +--- runtime: cycle=656.1k imports=3 allocs=17.0M(3.40%) +=== RUN TestSGT +--- PASS: TestSGT (0.00s) +--- runtime: cycle=666.1k imports=3 allocs=17.2M(3.44%) +=== RUN TestEq +--- PASS: TestEq (0.00s) +--- runtime: cycle=714.3k imports=3 allocs=18.2M(3.64%) +=== RUN TestIsUint64 +--- PASS: TestIsUint64 (0.00s) +--- runtime: cycle=719.5k imports=3 allocs=18.3M(3.67%) +=== RUN TestDec +=== RUN TestDec/zero +--- PASS: TestDec/zero (0.00s) +=== RUN TestDec/less_than_20_digits +--- PASS: TestDec/less_than_20_digits (0.00s) +=== RUN TestDec/max_possible_value +--- PASS: TestDec/max_possible_value (0.00s) +--- PASS: TestDec (0.00s) +--- runtime: cycle=735.2k imports=3 allocs=18.9M(3.78%) +ok ./ 0.61s \ No newline at end of file From 9549233073bd549866cdd9b0107e785de7dffba7 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 27 Mar 2024 14:39:04 +0900 Subject: [PATCH 20/23] refactor: remove unused functions --- examples/gno.land/p/demo/uint256/mod.gno | 18 ------------------ examples/gno.land/p/demo/uint256/utils.gno | 11 ----------- 2 files changed, 29 deletions(-) diff --git a/examples/gno.land/p/demo/uint256/mod.gno b/examples/gno.land/p/demo/uint256/mod.gno index 8fa101f9dde..f6ff0967e44 100644 --- a/examples/gno.land/p/demo/uint256/mod.gno +++ b/examples/gno.land/p/demo/uint256/mod.gno @@ -6,24 +6,6 @@ import ( // Some utility functions -func leadingZeros(x *Uint) (z int) { - var t int - z = bits.LeadingZeros64(x.arr[3]) - t = bits.LeadingZeros64(x.arr[2]) - if z == 64 { - z = t + 64 - } - t = bits.LeadingZeros64(x.arr[1]) - if z == 128 { - z = t + 128 - } - t = bits.LeadingZeros64(x.arr[0]) - if z == 192 { - z = t + 192 - } - return z -} - // Reciprocal computes a 320-bit value representing 1/m // // Notes: diff --git a/examples/gno.land/p/demo/uint256/utils.gno b/examples/gno.land/p/demo/uint256/utils.gno index e013696ab8f..969728f3369 100644 --- a/examples/gno.land/p/demo/uint256/utils.gno +++ b/examples/gno.land/p/demo/uint256/utils.gno @@ -178,14 +178,3 @@ func parseUint(s string, base int, bitSize int) (uint64, error) { return n, nil } - -func parseUintFromString(s string) (*Uint, error) { - if s == "0" { - return NewUint(0), nil - } - value, err := parseUint(s, 16, 64) - if err != nil { - return nil, err - } - return NewUint(value), nil -} From 36e26c81f82abf7a57ddbfb8797a26e82988ba16 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 27 Mar 2024 14:45:19 +0900 Subject: [PATCH 21/23] fix: remove int256 from uint256 --- examples/gno.land/p/demo/int256/gno.mod | 3 - examples/gno.land/p/demo/int256/int256.gno | 556 ------------------ .../gno.land/p/demo/int256/int256_test.gno | 1 - 3 files changed, 560 deletions(-) delete mode 100644 examples/gno.land/p/demo/int256/gno.mod delete mode 100644 examples/gno.land/p/demo/int256/int256.gno delete mode 100644 examples/gno.land/p/demo/int256/int256_test.gno diff --git a/examples/gno.land/p/demo/int256/gno.mod b/examples/gno.land/p/demo/int256/gno.mod deleted file mode 100644 index ef906c83c93..00000000000 --- a/examples/gno.land/p/demo/int256/gno.mod +++ /dev/null @@ -1,3 +0,0 @@ -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 deleted file mode 100644 index 0d67eb4e0b6..00000000000 --- a/examples/gno.land/p/demo/int256/int256.gno +++ /dev/null @@ -1,556 +0,0 @@ -// github.com/mempooler/int256 - -package int256 - -import ( - "gno.land/p/demo/uint256" -) - -var one = uint256.NewUint(1) - -type Int struct { - abs *uint256.Uint - neg bool -} - -func Zero() *Int { - return NewInt(0) -} - -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 { - if len(z.abs) == 0 { - return 0 - } - if z.neg { - return -1 - } - return 1 -} - -func New() *Int { - return &Int{ - abs: new(uint256.Uint), - } -} - -// SetInt64 sets z to x and returns z. -func (z *Int) SetInt64(x int64) *Int { - 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 { - if z.abs == nil { - panic("abs is nil") - } - z.abs = z.abs.SetUint64(x) - z.neg = false - return z -} - -// NewInt allocates and returns a new Int set to x. -func NewInt(x int64) *Int { - return New().SetInt64(x) -} - -func FromDecimal(s string) (*Int, error) { - return new(Int).SetString(s) -} - -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) { - origin := s - 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 -} - -// // setFromScanner implements SetString given an io.ByteScanner. -// // For documentation see comments of SetString. -// func (z *Int) setFromScanner(r io.ByteScanner, base int) (*Int, bool) { -// if _, _, err := z.scan(r, base); err != nil { -// return nil, false -// } -// // entire content must have been consumed -// if _, err := r.ReadByte(); err != io.EOF { -// return nil, false -// } -// return z, true // err == io.EOF => scan consumed all content of r -// } - -func (z *Int) Add(x, y *Int) *Int { - 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 -} - -// Sub sets z to the difference x-y and returns z. -func (z *Int) Sub(x, y *Int) *Int { - 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 -} - -// Mul sets z to the product x*y and returns z. -func (z *Int) Mul(x, y *Int) *Int { - z.abs = z.abs.Mul(x.abs, y.abs) - z.neg = x.neg != y.neg // 0 has no sign - return z -} - -// Rsh sets z = x >> n and returns z. -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(0)) - t = t.Rsh(t, n) - - _tmp := t.Add(t, NewInt(1)) - z.abs = _tmp.Abs() - z.neg = true - - 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. -// Quo implements truncated division (like Go); see QuoRem for more details. -func (z *Int) Quo(x, y *Int) *Int { - z.abs = z.abs.Div(x.abs, y.abs) - z.neg = len(z.abs) > 0 && 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. -// Rem implements truncated modulus (like Go); see QuoRem for more details. -func (z *Int) Rem(x, y *Int) *Int { - z.abs.Mod(x.abs, y.abs) - z.neg = len(z.abs) > 0 && x.neg // 0 has no sign - return z -} - -// 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 -} - -func (z *Int) Div(x, y *Int) *Int { - z.abs.Div(x.abs, y.abs) - if x.neg == y.neg { - z.neg = false - } else { - 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 -} - -// 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 -} - -// 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 - - // // TODO: implement - // big := new(big.Int).Or(x.ToBig(), y.ToBig()) - // z = MustFromBig(big) - return z -} - -// 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 := New() - // z := &Int{} - z.SetUint256(x) - return z -} - -// 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) -} - -// 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 -} - -// Clone creates a new Int identical to z -func (z *Int) Clone() *Int { - return &Int{z.abs.Clone(), z.neg} -} - -// 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 -} - -// Eq returns true if z == x -func (z *Int) Eq(x *Int) bool { - return (z.neg == x.neg) && z.abs.Eq(x.abs) -} - -// 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) - } - } -} - -// 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 -} - -// 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 -} - -// 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 -} - -// 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 -} - -// 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()) -} - -// 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 -} - -// NIL TO ZERO -func (z *Int) NilToZero() *Int { - if z == nil { - return NewInt(0) - } - return z -} - -func (z *Int) ToString() string { - if z == nil { - panic("int256: nil pointer") - } - - t := z.abs.Dec() - if z.neg { - return "-" + t - } - return t -} diff --git a/examples/gno.land/p/demo/int256/int256_test.gno b/examples/gno.land/p/demo/int256/int256_test.gno deleted file mode 100644 index e5b8059855c..00000000000 --- a/examples/gno.land/p/demo/int256/int256_test.gno +++ /dev/null @@ -1 +0,0 @@ -package int256 From 7ea5cce3f4512dbedab91b607bf4668cfdd2a745 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Wed, 27 Mar 2024 18:52:04 +0900 Subject: [PATCH 22/23] delete runtime metric file --- .../p/demo/uint256/runtime-metrics.txt | 130 ------------------ 1 file changed, 130 deletions(-) delete mode 100644 examples/gno.land/p/demo/uint256/runtime-metrics.txt diff --git a/examples/gno.land/p/demo/uint256/runtime-metrics.txt b/examples/gno.land/p/demo/uint256/runtime-metrics.txt deleted file mode 100644 index bba96334f98..00000000000 --- a/examples/gno.land/p/demo/uint256/runtime-metrics.txt +++ /dev/null @@ -1,130 +0,0 @@ -// gno test -v=true -print-runtime-metrics=true ./ -=== RUN TestAdd ---- PASS: TestAdd (0.00s) ---- runtime: cycle=25.9k imports=3 allocs=756.0k(0.15%) -=== RUN TestSub ---- PASS: TestSub (0.00s) ---- runtime: cycle=54.7k imports=3 allocs=1.5M(0.30%) -=== RUN TestMul ---- PASS: TestMul (0.00s) ---- runtime: cycle=76.5k imports=3 allocs=2.1M(0.42%) -=== RUN TestDiv ---- PASS: TestDiv (0.00s) ---- runtime: cycle=87.6k imports=3 allocs=2.5M(0.50%) -=== RUN TestMod ---- PASS: TestMod (0.00s) ---- runtime: cycle=100.8k imports=3 allocs=3.0M(0.59%) -=== RUN TestDivMod ---- PASS: TestDivMod (0.00s) ---- runtime: cycle=128.8k imports=3 allocs=4.0M(0.80%) -=== RUN TestNeg ---- PASS: TestNeg (0.00s) ---- runtime: cycle=203.1k imports=3 allocs=5.5M(1.10%) -=== RUN TestExp ---- PASS: TestExp (0.00s) ---- runtime: cycle=356.9k imports=3 allocs=8.7M(1.73%) -=== RUN TestOr -=== RUN TestOr/all_zeros ---- PASS: TestOr/all_zeros (0.00s) -=== RUN TestOr/all_ones ---- PASS: TestOr/all_ones (0.00s) -=== RUN TestOr/mixed ---- PASS: TestOr/mixed (0.00s) -=== RUN TestOr/one_operand_all_ones ---- PASS: TestOr/one_operand_all_ones (0.00s) ---- PASS: TestOr (0.00s) ---- runtime: cycle=364.2k imports=3 allocs=9.0M(1.80%) -=== RUN TestAnd -=== RUN TestAnd/all_zeros ---- PASS: TestAnd/all_zeros (0.00s) -=== RUN TestAnd/all_ones ---- PASS: TestAnd/all_ones (0.00s) -=== RUN TestAnd/mixed ---- PASS: TestAnd/mixed (0.00s) -=== RUN TestAnd/mixed_2 ---- PASS: TestAnd/mixed_2 (0.00s) -=== RUN TestAnd/mixed_3 ---- PASS: TestAnd/mixed_3 (0.00s) -=== RUN TestAnd/one_operand_zero ---- PASS: TestAnd/one_operand_zero (0.00s) -=== RUN TestAnd/one_operand_all_ones ---- PASS: TestAnd/one_operand_all_ones (0.00s) ---- PASS: TestAnd (0.00s) ---- runtime: cycle=376.6k imports=3 allocs=9.6M(1.92%) -=== RUN TestNot -=== RUN TestNot/all_zeros ---- PASS: TestNot/all_zeros (0.00s) -=== RUN TestNot/all_ones ---- PASS: TestNot/all_ones (0.00s) -=== RUN TestNot/mixed ---- PASS: TestNot/mixed (0.00s) ---- PASS: TestNot (0.00s) ---- runtime: cycle=381.2k imports=3 allocs=9.8M(1.97%) -=== RUN TestAndNot -=== RUN TestAndNot/all_zeros ---- PASS: TestAndNot/all_zeros (0.00s) -=== RUN TestAndNot/all_ones ---- PASS: TestAndNot/all_ones (0.00s) -=== RUN TestAndNot/mixed ---- PASS: TestAndNot/mixed (0.00s) -=== RUN TestAndNot/mixed_2 ---- PASS: TestAndNot/mixed_2 (0.00s) -=== RUN TestAndNot/mixed_3 ---- PASS: TestAndNot/mixed_3 (0.00s) -=== RUN TestAndNot/one_operand_zero ---- PASS: TestAndNot/one_operand_zero (0.00s) -=== RUN TestAndNot/one_operand_all_ones ---- PASS: TestAndNot/one_operand_all_ones (0.00s) ---- PASS: TestAndNot (0.00s) ---- runtime: cycle=393.6k imports=3 allocs=10.4M(2.08%) -=== RUN TestXor -=== RUN TestXor/all_zeros ---- PASS: TestXor/all_zeros (0.00s) -=== RUN TestXor/all_ones ---- PASS: TestXor/all_ones (0.00s) -=== RUN TestXor/mixed ---- PASS: TestXor/mixed (0.00s) -=== RUN TestXor/mixed_2 ---- PASS: TestXor/mixed_2 (0.00s) -=== RUN TestXor/mixed_3 ---- PASS: TestXor/mixed_3 (0.00s) -=== RUN TestXor/one_operand_zero ---- PASS: TestXor/one_operand_zero (0.00s) -=== RUN TestXor/one_operand_all_ones ---- PASS: TestXor/one_operand_all_ones (0.00s) ---- PASS: TestXor (0.00s) ---- runtime: cycle=406.0k imports=3 allocs=11.0M(2.20%) -=== RUN TestLsh ---- PASS: TestLsh (0.00s) ---- runtime: cycle=531.2k imports=3 allocs=13.8M(2.77%) -=== RUN TestRsh ---- PASS: TestRsh (0.00s) ---- runtime: cycle=636.1k imports=3 allocs=16.3M(3.25%) -=== RUN TestCmp ---- PASS: TestCmp (0.00s) ---- runtime: cycle=647.6k imports=3 allocs=16.7M(3.34%) -=== RUN TestIsZero ---- PASS: TestIsZero (0.00s) ---- runtime: cycle=650.3k imports=3 allocs=16.8M(3.36%) -=== RUN TestLtUint64 ---- PASS: TestLtUint64 (0.00s) ---- runtime: cycle=656.1k imports=3 allocs=17.0M(3.40%) -=== RUN TestSGT ---- PASS: TestSGT (0.00s) ---- runtime: cycle=666.1k imports=3 allocs=17.2M(3.44%) -=== RUN TestEq ---- PASS: TestEq (0.00s) ---- runtime: cycle=714.3k imports=3 allocs=18.2M(3.64%) -=== RUN TestIsUint64 ---- PASS: TestIsUint64 (0.00s) ---- runtime: cycle=719.5k imports=3 allocs=18.3M(3.67%) -=== RUN TestDec -=== RUN TestDec/zero ---- PASS: TestDec/zero (0.00s) -=== RUN TestDec/less_than_20_digits ---- PASS: TestDec/less_than_20_digits (0.00s) -=== RUN TestDec/max_possible_value ---- PASS: TestDec/max_possible_value (0.00s) ---- PASS: TestDec (0.00s) ---- runtime: cycle=735.2k imports=3 allocs=18.9M(3.78%) -ok ./ 0.61s \ No newline at end of file From 347258a9da3be5c41997bf53c1c204fd8944c06e Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Fri, 29 Mar 2024 12:26:16 +0900 Subject: [PATCH 23/23] remove unused file --- examples/gno.land/p/demo/uint256/uint256_test.gno | 1 - 1 file changed, 1 deletion(-) delete mode 100644 examples/gno.land/p/demo/uint256/uint256_test.gno diff --git a/examples/gno.land/p/demo/uint256/uint256_test.gno b/examples/gno.land/p/demo/uint256/uint256_test.gno deleted file mode 100644 index bd730accda3..00000000000 --- a/examples/gno.land/p/demo/uint256/uint256_test.gno +++ /dev/null @@ -1 +0,0 @@ -package uint256