Files
mev-beta/vendor/github.com/holiman/uint256/decimal.go

226 lines
6.7 KiB
Go

// uint256: Fixed size 256-bit math library
// Copyright 2020 uint256 Authors
// SPDX-License-Identifier: BSD-3-Clause
package uint256
import (
"io"
"strconv"
)
const twoPow256Sub1 = "115792089237316195423570985008687907853269984665640564039457584007913129639935"
// Dec returns the decimal representation of z.
func (z *Int) 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 = NewInt(10000000000000000000) // 20 digits
y = new(Int).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, rem Int
udivrem(quot[:], y[:], divisor, &rem)
y.Set(&quot) // 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):])
}
// PrettyDec returns the decimal representation of z, with thousands-separators.
func (z *Int) PrettyDec(separator byte) string {
if z.IsZero() {
return "0"
}
// See algorithm-description in Dec()
// This just also inserts comma while copying byte-for-byte instead
// of using copy().
var (
out = []byte("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
divisor = NewInt(10000000000000000000)
y = new(Int).Set(z) // copy to avoid modifying z
pos = len(out) - 1 // position to write to
buf = make([]byte, 0, 19) // buffer to write uint64:s to
comma = 0
)
for {
var quot, rem Int
udivrem(quot[:], y[:], divisor, &rem)
y.Set(&quot) // Set Q for next loop
buf = strconv.AppendUint(buf[:0], rem.Uint64(), 10)
for j := len(buf) - 1; j >= 0; j-- {
if comma == 3 {
out[pos] = separator
pos--
comma = 0
}
out[pos] = buf[j]
comma++
pos--
}
if y.IsZero() {
break
}
// Need to do zero-padding if we have more iterations coming
for j := 0; j < 19-len(buf); j++ {
if comma == 3 {
out[pos] = separator
pos--
comma = 0
}
comma++
pos--
}
}
return string(out[pos+1:])
}
// 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
}
// 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
}
// 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",
// - This method does not accept negative zero as valid, e.g "-0",
// - (this method does not accept any negative input as valid))
func (z *Int) 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
}
// multipliers holds the values that are needed for fromDecimal
var multipliers = [5]*Int{
nil, // represents first round, no multiplication needed
{10000000000000000000, 0, 0, 0}, // 10 ^ 19
{687399551400673280, 5421010862427522170, 0, 0}, // 10 ^ 38
{5332261958806667264, 17004971331911604867, 2938735877055718769, 0}, // 10 ^ 57
{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.
// Note: this method assumes that some basic validity-checks have already been performed
// on the input 'bs'. See SetFromDecimal.
func (z *Int) 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 io.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 = strconv.ParseUint(bs[remaining-19:remaining], 10, 64)
} else {
// Final round
num, err = strconv.ParseUint(bs, 10, 64)
}
if err != nil {
return err
}
// add that number to our running total
if i == 0 {
z.SetUint64(num)
} else {
base := NewInt(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
}