Skip to content

Commit

Permalink
added new utilities function, changed package name and moved to space…
Browse files Browse the repository at this point in the history
… based formating isntead of tab
  • Loading branch information
0xsimulacra committed Jan 17, 2025
1 parent 5a96414 commit 1f49de0
Show file tree
Hide file tree
Showing 11 changed files with 2,303 additions and 2,220 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

Wrap [uint256](https://github.com/holiman/uint256) fixed size 256-bit math library to allow perform with negative number.

Taken from [https://github.com/linhbkhn95/int256](https://github.com/linhbkhn95/int256) but with my own additions that I find usefull.
# Example usage:

### Expanding a number to 18 decimals
Expand Down
92 changes: 46 additions & 46 deletions big_int.go
Original file line number Diff line number Diff line change
@@ -1,63 +1,63 @@
package int256

import (
"math/big"
"math/big"

"github.com/holiman/uint256"
"github.com/holiman/uint256"
)

var negativeOneBigInt = big.NewInt(-1)
var zero = big.NewInt(0)

func (z *Int) ToBig() *big.Int {
b := z.abs.ToBig()
if z.neg {
return b.Mul(b, negativeOneBigInt)
}
return b
b := z.abs.ToBig()
if z.neg {
return b.Mul(b, negativeOneBigInt)
}
return b
}

func MustFromBig(x *big.Int) *Int {
big, overflow := FromBig(x)
if overflow {
panic("cannot parsing from big.Int")
}
return big
big, overflow := FromBig(x)
if overflow {
panic("cannot parsing from big.Int")
}
return big
}

func FromBig(x *big.Int) (*Int, bool) {
num := x
neg := false
if x.Cmp(zero) == -1 {
num = new(big.Int).Mul(x, negativeOneBigInt)
neg = true
}
abs, overflow := uint256.FromBig(num)
/*
type Int [4]uint64
Currently, uint26 has maxWord is 4
bigInt has len(words) that can great than 4. So we can receive overflow error.
words := b.Bits()
overflow := len(words) > maxWords
ref from uint256 code
https://github.com/holiman/uint256/blob/master/conversion.go#L202
*/
if overflow {
abs, err := uint256.FromDecimal(x.String())
if err != nil {
return nil, overflow
}
neg = x.Sign() < 0
return &Int{
abs,
neg,
}, true
}
return &Int{
abs,
neg,
}, false
num := x
neg := false
if x.Cmp(zero) == -1 {
num = new(big.Int).Mul(x, negativeOneBigInt)
neg = true
}
abs, overflow := uint256.FromBig(num)
/*
type Int [4]uint64
Currently, uint26 has maxWord is 4
bigInt has len(words) that can great than 4. So we can receive overflow error.
words := b.Bits()
overflow := len(words) > maxWords
ref from uint256 code
https://github.com/holiman/uint256/blob/master/conversion.go#L202
*/
if overflow {
abs, err := uint256.FromDecimal(x.String())
if err != nil {
return nil, overflow
}
neg = x.Sign() < 0
return &Int{
abs,
neg,
}, true
}
return &Int{
abs,
neg,
}, false
}
181 changes: 105 additions & 76 deletions conversion.go
Original file line number Diff line number Diff line change
@@ -1,47 +1,49 @@
package int256

import (
"database/sql/driver"
"errors"
"strings"
"database/sql/driver"
"errors"
"strings"

"github.com/holiman/uint256"
)

// SetFromDecimal sets z from the given string, interpreted as a decimal number.
// OBS! This method is _not_ strictly identical to the (*big.Int).SetString(..., 10) method.
// Notable differences:
// - This method does not accept underscore input, e.g. "100_000"
func (z *Int) SetFromDecimal(decimal string) (err error) {
z.initiateAbs()
if strings.HasPrefix(decimal, "-") {
z.neg = true
decimal = decimal[1:]
// take into account the double sign at the beginning which create an error in big.Int
if strings.HasPrefix(decimal, "+") || strings.HasPrefix(decimal, "-") {
return ErrMultipleSignAtStart
}
}
return z.abs.SetFromDecimal(decimal)
z.initiateAbs()
if strings.HasPrefix(decimal, "-") {
z.neg = true
decimal = decimal[1:]
// take into account the double sign at the beginning which create an error in big.Int
if strings.HasPrefix(decimal, "+") || strings.HasPrefix(decimal, "-") {
return ErrMultipleSignAtStart
}
}
return z.abs.SetFromDecimal(decimal)
}

// FromDecimal is a convenience-constructor to create an Int from a
// decimal (base 10) string. Numbers larger than 256 bits are not accepted.
func FromDecimal(decimal string) (*Int, error) {
var z Int
if err := z.SetFromDecimal(decimal); err != nil {
return nil, err
}
return &z, nil
var z Int
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) *Int {
var z Int
if err := z.SetFromDecimal(decimal); err != nil {
panic(err)
}
return &z
var z Int
if err := z.SetFromDecimal(decimal); err != nil {
panic(err)
}
return &z
}

// SetFromHex sets z from the given string, interpreted as a hexadecimal number.
Expand All @@ -52,81 +54,81 @@ func MustFromDecimal(decimal string) *Int {
// - This method does not accept underscore input, e.g. "100_000",
// - negative value should be prefixed with "-" like "-0x0"
func (z *Int) SetFromHex(hex string) error {
z.initiateAbs()
if strings.HasPrefix(hex, "-") {
z.neg = true
hex = hex[1:]
// take into account the double sign at the beginning which create an error in big.Int
if strings.HasPrefix(hex, "+") || strings.HasPrefix(hex, "-") {
return ErrMultipleSignAtStart
}
}
return z.abs.SetFromHex(hex)
z.initiateAbs()
if strings.HasPrefix(hex, "-") {
z.neg = true
hex = hex[1:]
// take into account the double sign at the beginning which create an error in big.Int
if strings.HasPrefix(hex, "+") || strings.HasPrefix(hex, "-") {
return ErrMultipleSignAtStart
}
}
return z.abs.SetFromHex(hex)
}

// FromHex is a convenience-constructor to create an Int from
// a hexadecimal string. The string is required to be '0x' or "-0x"-prefixed
// Numbers larger than 256 bits are not accepted.
func FromHex(hex string) (*Int, error) {
var z Int
if err := z.SetFromHex(hex); err != nil {
return nil, err
}
return &z, nil
var z Int
if err := z.SetFromHex(hex); err != nil {
return nil, err
}
return &z, nil
}

// MustFromHex is a convenience-constructor to create an Int from
// a hexadecimal string.
// Returns a new Int and panics if any error occurred.
func MustFromHex(hex string) *Int {
var z Int
if err := z.SetFromHex(hex); err != nil {
panic(err)
}
return &z
var z Int
if err := z.SetFromHex(hex); err != nil {
panic(err)
}
return &z
}

// Hex encodes z in 0x-prefixed or -0x-prefixed hexadecimal form.
func (z *Int) Hex() string {
z.initiateAbs()
if z.abs.IsZero() {
return "0x0"
}
if z.neg {
return "-" + z.abs.Hex()
}
return z.abs.Hex()
z.initiateAbs()
if z.abs.IsZero() {
return "0x0"
}
if z.neg {
return "-" + z.abs.Hex()
}
return z.abs.Hex()
}

// Dec returns the decimal representation of z.
func (z *Int) Dec() string {
z.initiateAbs()
if z.abs.IsZero() {
return "0"
}
if z.neg {
return "-" + z.abs.Dec()
}
return z.abs.Dec()
z.initiateAbs()
if z.abs.IsZero() {
return "0"
}
if z.neg {
return "-" + z.abs.Dec()
}
return z.abs.Dec()
}

// MarshalText implements encoding.TextMarshaler
// MarshalText marshals using the decimal representation
func (z *Int) MarshalText() ([]byte, error) {
return []byte(z.Dec()), nil
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, -0X, 0x or 0X
func (z *Int) UnmarshalText(input []byte) error {
if len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') {
return z.SetFromHex(string(input))
}
if len(input) >= 3 && input[0] == '-' && input[1] == '0' && (input[2] == 'x' || input[2] == 'X') {
return z.SetFromHex(string(input))
}
return z.SetFromDecimal(string(input))
if len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') {
return z.SetFromHex(string(input))
}
if len(input) >= 3 && input[0] == '-' && input[1] == '0' && (input[2] == 'x' || input[2] == 'X') {
return z.SetFromHex(string(input))
}
return z.SetFromDecimal(string(input))
}

// MarshalJSON implements json.Marshaler.
Expand All @@ -137,25 +139,25 @@ func (z *Int) UnmarshalText(input []byte) error {
// integer space. Thus, U256 uses string-format, which is not compatible with
// big.int (big.Int refuses to unmarshal a string representation).
func (z *Int) MarshalJSON() ([]byte, error) {
return []byte(`"` + z.Dec() + `"`), nil
return []byte(`"` + z.Dec() + `"`), nil
}

// UnmarshalJSON implements json.Unmarshaler. UnmarshalJSON accepts either
// - Quoted string: either hexadecimal OR decimal
// - For hexadecimal, the input _must_ be prefixed with -0x, -0X, 0x or 0X
// - Not quoted string: only decimal
func (z *Int) UnmarshalJSON(input []byte) error {
if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' {
// if not quoted, it must be decimal
return z.SetFromDecimal(string(input))
}
if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' {
// if not quoted, it must be decimal
return z.SetFromDecimal(string(input))
}

return z.UnmarshalText(input[1 : len(input)-1])
return z.UnmarshalText(input[1 : len(input)-1])
}

// String returns the decimal encoding of z
func (z *Int) String() string {
return z.Dec()
return z.Dec()
}

// Value implements the database/sql/driver Valuer interface.
Expand All @@ -164,9 +166,36 @@ func (z *Int) String() string {
// In MariaDB/MySQL, this will work with the Numeric/Decimal types up to 65 digits, however any more and you should use either VarChar or Char(79)
// In SqLite, use TEXT
func (z *Int) Value() (driver.Value, error) {
return z.Dec(), nil
return z.Dec(), nil
}

var (
ErrMultipleSignAtStart = errors.New("multiple sign at the beginning")
ErrMultipleSignAtStart = errors.New("multiple sign at the beginning")
)

// CmpU compares z (Int) and x(Uint) and returns:
//
// -1 if z < x
// 0 if z == x
// +1 if z > x
func (z *Int) CmpU(x *uint256.Int) int {
if z.abs.IsZero() && x.IsZero() {
return 0
}
if z.neg {
return -1
}
return z.abs.Cmp(x)
}

// Abs Absolute value of z having the type uint256.Int
func (z *Int) Abs() *uint256.Int {
return z.abs.Clone()
}

// FromUInt256 create a int256.Int from a uint256.Int
func FromUInt256(x *uint256.Int) *Int {
z := new(Int)
z.abs = x.Clone()
return z
}
Loading

0 comments on commit 1f49de0

Please sign in to comment.