mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 23:18:15 -05:00
Move Shared Packages into Math/ and IO/ (#9622)
* amend * building * build * userprompt * imports * build val * gaz * io file Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
19
math/BUILD.bazel
Normal file
19
math/BUILD.bazel
Normal file
@@ -0,0 +1,19 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["math_helper.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/math",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["@com_github_thomaso_mirodin_intmath//u64:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "small",
|
||||
srcs = ["math_helper_test.go"],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
"//shared/testutil/require:go_default_library",
|
||||
],
|
||||
)
|
||||
114
math/math_helper.go
Normal file
114
math/math_helper.go
Normal file
@@ -0,0 +1,114 @@
|
||||
// Package math includes important helpers for Ethereum such as fast integer square roots.
|
||||
package math
|
||||
|
||||
import (
|
||||
"errors"
|
||||
stdmath "math"
|
||||
"math/bits"
|
||||
|
||||
"github.com/thomaso-mirodin/intmath/u64"
|
||||
)
|
||||
|
||||
// Common square root values.
|
||||
var squareRootTable = map[uint64]uint64{
|
||||
4: 2,
|
||||
16: 4,
|
||||
64: 8,
|
||||
256: 16,
|
||||
1024: 32,
|
||||
4096: 64,
|
||||
16384: 128,
|
||||
65536: 256,
|
||||
262144: 512,
|
||||
1048576: 1024,
|
||||
4194304: 2048,
|
||||
}
|
||||
|
||||
// IntegerSquareRoot defines a function that returns the
|
||||
// largest possible integer root of a number using go's standard library.
|
||||
func IntegerSquareRoot(n uint64) uint64 {
|
||||
if v, ok := squareRootTable[n]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
// Golang floating point precision may be lost above 52 bits, so we use a
|
||||
// non floating point method. u64.Sqrt is about x2.5 slower than math.Sqrt.
|
||||
if n >= 1<<52 {
|
||||
return u64.Sqrt(n)
|
||||
}
|
||||
|
||||
return uint64(stdmath.Sqrt(float64(n)))
|
||||
}
|
||||
|
||||
// CeilDiv8 divides the input number by 8
|
||||
// and takes the ceiling of that number.
|
||||
func CeilDiv8(n int) int {
|
||||
ret := n / 8
|
||||
if n%8 > 0 {
|
||||
ret++
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// IsPowerOf2 returns true if n is an
|
||||
// exact power of two. False otherwise.
|
||||
func IsPowerOf2(n uint64) bool {
|
||||
return n != 0 && (n&(n-1)) == 0
|
||||
}
|
||||
|
||||
// PowerOf2 returns an integer that is the provided
|
||||
// exponent of 2. Can only return powers of 2 till 63,
|
||||
// after that it overflows
|
||||
func PowerOf2(n uint64) uint64 {
|
||||
if n >= 64 {
|
||||
panic("integer overflow")
|
||||
}
|
||||
return 1 << n
|
||||
}
|
||||
|
||||
// Max returns the larger integer of the two
|
||||
// given ones.This is used over the Max function
|
||||
// in the standard math library because that max function
|
||||
// has to check for some special floating point cases
|
||||
// making it slower by a magnitude of 10.
|
||||
func Max(a, b uint64) uint64 {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Min returns the smaller integer of the two
|
||||
// given ones. This is used over the Min function
|
||||
// in the standard math library because that min function
|
||||
// has to check for some special floating point cases
|
||||
// making it slower by a magnitude of 10.
|
||||
func Min(a, b uint64) uint64 {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Mul64 multiples 2 64-bit unsigned integers and checks if they
|
||||
// lead to an overflow. If they do not, it returns the result
|
||||
// without an error.
|
||||
func Mul64(a, b uint64) (uint64, error) {
|
||||
overflows, val := bits.Mul64(a, b)
|
||||
if overflows > 0 {
|
||||
return 0, errors.New("multiplication overflows")
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// Add64 adds 2 64-bit unsigned integers and checks if they
|
||||
// lead to an overflow. If they do not, it returns the result
|
||||
// without an error.
|
||||
func Add64(a, b uint64) (uint64, error) {
|
||||
res, carry := bits.Add64(a, b, 0 /* carry */)
|
||||
if carry > 0 {
|
||||
return 0, errors.New("addition overflows")
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
334
math/math_helper_test.go
Normal file
334
math/math_helper_test.go
Normal file
@@ -0,0 +1,334 @@
|
||||
package math_test
|
||||
|
||||
import (
|
||||
stdmath "math"
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/math"
|
||||
"github.com/prysmaticlabs/prysm/shared/testutil/require"
|
||||
)
|
||||
|
||||
func TestIntegerSquareRoot(t *testing.T) {
|
||||
tt := []struct {
|
||||
number uint64
|
||||
root uint64
|
||||
}{
|
||||
{
|
||||
number: 20,
|
||||
root: 4,
|
||||
},
|
||||
{
|
||||
number: 200,
|
||||
root: 14,
|
||||
},
|
||||
{
|
||||
number: 1987,
|
||||
root: 44,
|
||||
},
|
||||
{
|
||||
number: 34989843,
|
||||
root: 5915,
|
||||
},
|
||||
{
|
||||
number: 97282,
|
||||
root: 311,
|
||||
},
|
||||
{
|
||||
number: 1 << 32,
|
||||
root: 1 << 16,
|
||||
},
|
||||
{
|
||||
number: (1 << 32) + 1,
|
||||
root: 1 << 16,
|
||||
},
|
||||
{
|
||||
number: 1 << 33,
|
||||
root: 92681,
|
||||
},
|
||||
{
|
||||
number: 1 << 60,
|
||||
root: 1 << 30,
|
||||
},
|
||||
{
|
||||
number: 1 << 53,
|
||||
root: 94906265,
|
||||
},
|
||||
{
|
||||
number: 1 << 62,
|
||||
root: 1 << 31,
|
||||
},
|
||||
{
|
||||
number: 1024,
|
||||
root: 32,
|
||||
},
|
||||
{
|
||||
number: 4,
|
||||
root: 2,
|
||||
},
|
||||
{
|
||||
number: 16,
|
||||
root: 4,
|
||||
},
|
||||
{
|
||||
number: 5508423000000000,
|
||||
root: 74218750,
|
||||
},
|
||||
{
|
||||
number: 4503599761588224,
|
||||
root: 67108864,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testVals := range tt {
|
||||
require.Equal(t, testVals.root, math.IntegerSquareRoot(testVals.number))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIntegerSquareRootBelow52Bits(b *testing.B) {
|
||||
val := uint64(1 << 33)
|
||||
for i := 0; i < b.N; i++ {
|
||||
require.Equal(b, uint64(92681), math.IntegerSquareRoot(val))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIntegerSquareRootAbove52Bits(b *testing.B) {
|
||||
val := uint64(1 << 62)
|
||||
for i := 0; i < b.N; i++ {
|
||||
require.Equal(b, uint64(1<<31), math.IntegerSquareRoot(val))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIntegerSquareRoot_WithDatatable(b *testing.B) {
|
||||
val := uint64(1024)
|
||||
for i := 0; i < b.N; i++ {
|
||||
require.Equal(b, uint64(32), math.IntegerSquareRoot(val))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCeilDiv8(t *testing.T) {
|
||||
tests := []struct {
|
||||
number int
|
||||
div8 int
|
||||
}{
|
||||
{
|
||||
number: 20,
|
||||
div8: 3,
|
||||
},
|
||||
{
|
||||
number: 200,
|
||||
div8: 25,
|
||||
},
|
||||
{
|
||||
number: 1987,
|
||||
div8: 249,
|
||||
},
|
||||
{
|
||||
number: 1,
|
||||
div8: 1,
|
||||
},
|
||||
{
|
||||
number: 97282,
|
||||
div8: 12161,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
require.Equal(t, tt.div8, math.CeilDiv8(tt.number))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsPowerOf2(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b bool
|
||||
}{
|
||||
{
|
||||
a: 2,
|
||||
b: true,
|
||||
},
|
||||
{
|
||||
a: 64,
|
||||
b: true,
|
||||
},
|
||||
{
|
||||
a: 100,
|
||||
b: false,
|
||||
},
|
||||
{
|
||||
a: 1024,
|
||||
b: true,
|
||||
},
|
||||
{
|
||||
a: 0,
|
||||
b: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
require.Equal(t, tt.b, math.IsPowerOf2(tt.a))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPowerOf2(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b uint64
|
||||
}{
|
||||
{
|
||||
a: 3,
|
||||
b: 8,
|
||||
},
|
||||
{
|
||||
a: 20,
|
||||
b: 1048576,
|
||||
},
|
||||
{
|
||||
a: 11,
|
||||
b: 2048,
|
||||
},
|
||||
{
|
||||
a: 8,
|
||||
b: 256,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
require.Equal(t, tt.b, math.PowerOf2(tt.a))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b uint64
|
||||
result uint64
|
||||
}{
|
||||
{
|
||||
a: 10,
|
||||
b: 8,
|
||||
result: 10,
|
||||
},
|
||||
{
|
||||
a: 300,
|
||||
b: 256,
|
||||
result: 300,
|
||||
},
|
||||
{
|
||||
a: 1200,
|
||||
b: 1024,
|
||||
result: 1200,
|
||||
},
|
||||
{
|
||||
a: 4500,
|
||||
b: 4096,
|
||||
result: 4500,
|
||||
},
|
||||
{
|
||||
a: 9999,
|
||||
b: 9999,
|
||||
result: 9999,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
require.Equal(t, tt.result, math.Max(tt.a, tt.b))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
a uint64
|
||||
b uint64
|
||||
result uint64
|
||||
}{
|
||||
{
|
||||
a: 10,
|
||||
b: 8,
|
||||
result: 8,
|
||||
},
|
||||
{
|
||||
a: 300,
|
||||
b: 256,
|
||||
result: 256,
|
||||
},
|
||||
{
|
||||
a: 1200,
|
||||
b: 1024,
|
||||
result: 1024,
|
||||
},
|
||||
{
|
||||
a: 4500,
|
||||
b: 4096,
|
||||
result: 4096,
|
||||
},
|
||||
{
|
||||
a: 9999,
|
||||
b: 9999,
|
||||
result: 9999,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
require.Equal(t, tt.result, math.Min(tt.a, tt.b))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMul64(t *testing.T) {
|
||||
type args struct {
|
||||
a uint64
|
||||
b uint64
|
||||
}
|
||||
tests := []struct {
|
||||
args args
|
||||
res uint64
|
||||
err bool
|
||||
}{
|
||||
{args: args{0, 1}, res: 0, err: false},
|
||||
{args: args{1 << 32, 1}, res: 1 << 32, err: false},
|
||||
{args: args{1 << 32, 100}, res: 429496729600, err: false},
|
||||
{args: args{1 << 32, 1 << 31}, res: 9223372036854775808, err: false},
|
||||
{args: args{1 << 32, 1 << 32}, res: 0, err: true},
|
||||
{args: args{1 << 62, 2}, res: 9223372036854775808, err: false},
|
||||
{args: args{1 << 62, 4}, res: 0, err: true},
|
||||
{args: args{1 << 63, 1}, res: 9223372036854775808, err: false},
|
||||
{args: args{1 << 63, 2}, res: 0, err: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got, err := math.Mul64(tt.args.a, tt.args.b)
|
||||
if tt.err && err == nil {
|
||||
t.Errorf("Mul64() Expected Error = %v, want error", tt.err)
|
||||
continue
|
||||
}
|
||||
if tt.res != got {
|
||||
t.Errorf("Mul64() %v, want %v", got, tt.res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd64(t *testing.T) {
|
||||
type args struct {
|
||||
a uint64
|
||||
b uint64
|
||||
}
|
||||
tests := []struct {
|
||||
args args
|
||||
res uint64
|
||||
err bool
|
||||
}{
|
||||
{args: args{0, 1}, res: 1, err: false},
|
||||
{args: args{1 << 32, 1}, res: 4294967297, err: false},
|
||||
{args: args{1 << 32, 100}, res: 4294967396, err: false},
|
||||
{args: args{1 << 31, 1 << 31}, res: 4294967296, err: false},
|
||||
{args: args{1 << 63, 1 << 63}, res: 0, err: true},
|
||||
{args: args{1 << 63, 1}, res: 9223372036854775809, err: false},
|
||||
{args: args{stdmath.MaxUint64, 1}, res: 0, err: true},
|
||||
{args: args{stdmath.MaxUint64, 0}, res: stdmath.MaxUint64, err: false},
|
||||
{args: args{1 << 63, 2}, res: 9223372036854775810, err: false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got, err := math.Add64(tt.args.a, tt.args.b)
|
||||
if tt.err && err == nil {
|
||||
t.Errorf("Add64() Expected Error = %v, want error", tt.err)
|
||||
continue
|
||||
}
|
||||
if tt.res != got {
|
||||
t.Errorf("Add64() %v, want %v", got, tt.res)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user