From ee3813ec6d6350cec0adb79a24666f0dc54a60a5 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Sun, 28 Jan 2024 16:15:24 +0530 Subject: [PATCH 1/5] Introduce Sqrt on NumberType --- runtime/interpreter/big.go | 29 +++++++++ runtime/interpreter/value.go | 121 +++++++++++++++++++++++++++++++++++ runtime/stdlib/builtin.go | 1 + runtime/stdlib/math.go | 71 ++++++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 runtime/stdlib/math.go diff --git a/runtime/interpreter/big.go b/runtime/interpreter/big.go index efb359d500..ecb4ce29ea 100644 --- a/runtime/interpreter/big.go +++ b/runtime/interpreter/big.go @@ -22,6 +22,7 @@ import ( "math/big" "github.com/onflow/cadence/runtime/errors" + "github.com/onflow/cadence/runtime/sema" ) func SignedBigIntToBigEndianBytes(bigInt *big.Int) []byte { @@ -140,3 +141,31 @@ func BigEndianBytesToSignedBigInt(b []byte) *big.Int { func BigEndianBytesToUnsignedBigInt(b []byte) *big.Int { return new(big.Int).SetBytes(b) } + +func BigIntSqrt(interpreter *Interpreter, value *big.Int, locationRange LocationRange) UFix64Value { + if value.Sign() < 0 { + panic(UnderflowError{ + LocationRange: locationRange, + }) + } + + valueFloat := new(big.Float).SetPrec(64).SetInt(value) + res := new(big.Float).SetPrec(64).Sqrt(valueFloat) + + valueGetter := func() uint64 { + res.Mul(res, new(big.Float).SetPrec(64).SetInt(sema.Fix64FactorBig)) + + resInt := new(big.Int) + res.Int(resInt) + + if !resInt.IsUint64() || resInt.Uint64() > sema.UFix64TypeMaxInt { + panic(OverflowError{ + LocationRange: locationRange, + }) + } + + return resInt.Uint64() + } + + return NewUFix64Value(interpreter, valueGetter) +} diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 431a3741e4..76f36316ee 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -3405,6 +3405,7 @@ type NumberValue interface { Div(interpreter *Interpreter, other NumberValue, locationRange LocationRange) NumberValue SaturatingDiv(interpreter *Interpreter, other NumberValue, locationRange LocationRange) NumberValue ToBigEndianBytes() []byte + Sqrt(*Interpreter, LocationRange) UFix64Value } func getNumberValueMember(interpreter *Interpreter, v NumberValue, name string, typ sema.Type, locationRange LocationRange) Value { @@ -3841,6 +3842,11 @@ func (v IntValue) SaturatingDiv(interpreter *Interpreter, other NumberValue, loc return v.Div(interpreter, other, locationRange) } +func (v IntValue) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := v.ToBigInt(interpreter) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v IntValue) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(IntValue) if !ok { @@ -4498,6 +4504,12 @@ func (v Int8Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, lo return NewInt8Value(interpreter, valueGetter) } +func (v Int8Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int) + val.SetInt64(int64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Int8Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Int8Value) if !ok { @@ -5138,6 +5150,12 @@ func (v Int16Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, l return NewInt16Value(interpreter, valueGetter) } +func (v Int16Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int) + val.SetInt64(int64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Int16Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Int16Value) if !ok { @@ -5781,6 +5799,12 @@ func (v Int32Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, l return NewInt32Value(interpreter, valueGetter) } +func (v Int32Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int) + val.SetInt64(int64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Int32Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Int32Value) if !ok { @@ -6423,6 +6447,12 @@ func (v Int64Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, l return NewInt64Value(interpreter, valueGetter) } +func (v Int64Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int) + val.SetInt64(int64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Int64Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Int64Value) if !ok { @@ -7120,6 +7150,11 @@ func (v Int128Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, return NewInt128ValueFromBigInt(interpreter, valueGetter) } +func (v Int128Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := v.ToBigInt(interpreter) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Int128Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Int128Value) if !ok { @@ -7862,6 +7897,11 @@ func (v Int256Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, return NewInt256ValueFromBigInt(interpreter, valueGetter) } +func (v Int256Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := v.ToBigInt(interpreter) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Int256Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Int256Value) if !ok { @@ -8514,6 +8554,11 @@ func (v UIntValue) SaturatingDiv(interpreter *Interpreter, other NumberValue, lo return v.Div(interpreter, other, locationRange) } +func (v UIntValue) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := v.ToBigInt(interpreter) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v UIntValue) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(UIntValue) if !ok { @@ -9078,6 +9123,12 @@ func (v UInt8Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, l return v.Div(interpreter, other, locationRange) } +func (v UInt8Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int) + val.SetUint64(uint64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v UInt8Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(UInt8Value) if !ok { @@ -9669,6 +9720,12 @@ func (v UInt16Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, return v.Div(interpreter, other, locationRange) } +func (v UInt16Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int) + val.SetUint64(uint64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v UInt16Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(UInt16Value) if !ok { @@ -10211,6 +10268,12 @@ func (v UInt32Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, return v.Div(interpreter, other, locationRange) } +func (v UInt32Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int) + val.SetUint64(uint64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v UInt32Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(UInt32Value) if !ok { @@ -10782,6 +10845,11 @@ func (v UInt64Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, return v.Div(interpreter, other, locationRange) } +func (v UInt64Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := v.ToBigInt(interpreter) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v UInt64Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(UInt64Value) if !ok { @@ -11400,6 +11468,11 @@ func (v UInt128Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, return v.Div(interpreter, other, locationRange) } +func (v UInt128Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := v.ToBigInt(interpreter) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v UInt128Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(UInt128Value) if !ok { @@ -12077,6 +12150,11 @@ func (v UInt256Value) SaturatingDiv(interpreter *Interpreter, other NumberValue, return v.Div(interpreter, other, locationRange) } +func (v UInt256Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := v.ToBigInt(interpreter) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v UInt256Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(UInt256Value) if !ok { @@ -12581,6 +12659,12 @@ func (v Word8Value) SaturatingDiv(*Interpreter, NumberValue, LocationRange) Numb panic(errors.NewUnreachableError()) } +func (v Word8Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int) + val.SetUint64(uint64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Word8Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Word8Value) if !ok { @@ -13017,6 +13101,12 @@ func (v Word16Value) SaturatingDiv(*Interpreter, NumberValue, LocationRange) Num panic(errors.NewUnreachableError()) } +func (v Word16Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int) + val.SetUint64(uint64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Word16Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Word16Value) if !ok { @@ -13456,6 +13546,12 @@ func (v Word32Value) SaturatingDiv(*Interpreter, NumberValue, LocationRange) Num panic(errors.NewUnreachableError()) } +func (v Word32Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int) + val.SetUint64(uint64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Word32Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Word32Value) if !ok { @@ -13921,6 +14017,11 @@ func (v Word64Value) SaturatingDiv(*Interpreter, NumberValue, LocationRange) Num panic(errors.NewUnreachableError()) } +func (v Word64Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := v.ToBigInt(interpreter) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Word64Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Word64Value) if !ok { @@ -14438,6 +14539,11 @@ func (v Word128Value) SaturatingDiv(_ *Interpreter, _ NumberValue, _ LocationRan panic(errors.NewUnreachableError()) } +func (v Word128Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := v.ToBigInt(interpreter) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Word128Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Word128Value) if !ok { @@ -15018,6 +15124,11 @@ func (v Word256Value) SaturatingDiv(_ *Interpreter, _ NumberValue, _ LocationRan panic(errors.NewUnreachableError()) } +func (v Word256Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := v.ToBigInt(interpreter) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Word256Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Word256Value) if !ok { @@ -15705,6 +15816,11 @@ func (v Fix64Value) Mod(interpreter *Interpreter, other NumberValue, locationRan ) } +func (v Fix64Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int).SetInt64(int64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v Fix64Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(Fix64Value) if !ok { @@ -16228,6 +16344,11 @@ func (v UFix64Value) Mod(interpreter *Interpreter, other NumberValue, locationRa ) } +func (v UFix64Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { + val := new(big.Int).SetUint64(uint64(v)) + return BigIntSqrt(interpreter, val, locationRange) +} + func (v UFix64Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { o, ok := other.(UFix64Value) if !ok { diff --git a/runtime/stdlib/builtin.go b/runtime/stdlib/builtin.go index ea4adbb0a9..ddaf56a5f3 100644 --- a/runtime/stdlib/builtin.go +++ b/runtime/stdlib/builtin.go @@ -45,6 +45,7 @@ func DefaultStandardLibraryValues(handler StandardLibraryHandler) []StandardLibr SignatureAlgorithmConstructor, RLPContract, InclusiveRangeConstructorFunction, + SqrtFunction, NewLogFunction(handler), NewRevertibleRandomFunction(handler), NewGetBlockFunction(handler), diff --git a/runtime/stdlib/math.go b/runtime/stdlib/math.go new file mode 100644 index 0000000000..1ed2c12fa3 --- /dev/null +++ b/runtime/stdlib/math.go @@ -0,0 +1,71 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package stdlib + +import ( + "github.com/onflow/cadence/runtime/errors" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/sema" +) + +// SqrtFunction + +const sqrtFunctionDocString = `TODO` + +var sqrtFunctionType = func() *sema.FunctionType { + typeParameter := &sema.TypeParameter{ + Name: "T", + TypeBound: sema.NumberType, + } + + typeAnnotation := sema.NewTypeAnnotation( + &sema.GenericType{ + TypeParameter: typeParameter, + }, + ) + + return &sema.FunctionType{ + TypeParameters: []*sema.TypeParameter{ + typeParameter, + }, + Parameters: []sema.Parameter{ + { + Label: sema.ArgumentLabelNotRequired, + Identifier: "value", + TypeAnnotation: typeAnnotation, + }, + }, + ReturnTypeAnnotation: sema.NewTypeAnnotation(sema.UFix64Type), + } +}() + +var SqrtFunction = NewStandardLibraryFunction( + "Sqrt", + sqrtFunctionType, + sqrtFunctionDocString, + func(invocation interpreter.Invocation) interpreter.Value { + value, ok := invocation.Arguments[0].(interpreter.NumberValue) + + if !ok { + panic(errors.NewUnreachableError()) + } + + return value.Sqrt(invocation.Interpreter, invocation.LocationRange) + }, +) From 90b485fe2ada7dc3b752981a22a97bf9f4044d7c Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Mon, 5 Feb 2024 20:14:35 +0530 Subject: [PATCH 2/5] Define Math package and implement tests --- fixedpoint/check.go | 1 + runtime/interpreter/big.go | 8 +- runtime/interpreter/value.go | 11 +- runtime/math_test.go | 354 +++++++++++++++++++++++++++++ runtime/stdlib/builtin.go | 2 +- runtime/stdlib/math.go | 72 +++++- runtime/tests/checker/math_test.go | 122 ++++++++++ 7 files changed, 556 insertions(+), 14 deletions(-) create mode 100644 runtime/math_test.go create mode 100644 runtime/tests/checker/math_test.go diff --git a/fixedpoint/check.go b/fixedpoint/check.go index 7de388b61e..10c6040731 100644 --- a/fixedpoint/check.go +++ b/fixedpoint/check.go @@ -25,6 +25,7 @@ import ( const Fix64Scale = 8 const Fix64Factor = 100_000_000 +const Fix64FactorSqrt = 10_000 // Fix64 diff --git a/runtime/interpreter/big.go b/runtime/interpreter/big.go index ecb4ce29ea..e9731f97f7 100644 --- a/runtime/interpreter/big.go +++ b/runtime/interpreter/big.go @@ -149,16 +149,16 @@ func BigIntSqrt(interpreter *Interpreter, value *big.Int, locationRange Location }) } - valueFloat := new(big.Float).SetPrec(64).SetInt(value) - res := new(big.Float).SetPrec(64).Sqrt(valueFloat) + valueFloat := new(big.Float).SetPrec(256).SetInt(value) + res := new(big.Float).SetPrec(256).SetMode(big.ToZero).Sqrt(valueFloat) valueGetter := func() uint64 { - res.Mul(res, new(big.Float).SetPrec(64).SetInt(sema.Fix64FactorBig)) + res.Mul(res, new(big.Float).SetPrec(256).SetInt(sema.Fix64FactorBig)) resInt := new(big.Int) res.Int(resInt) - if !resInt.IsUint64() || resInt.Uint64() > sema.UFix64TypeMaxInt { + if !resInt.IsUint64() { panic(OverflowError{ LocationRange: locationRange, }) diff --git a/runtime/interpreter/value.go b/runtime/interpreter/value.go index 76f36316ee..3d36c37e05 100644 --- a/runtime/interpreter/value.go +++ b/runtime/interpreter/value.go @@ -35,6 +35,7 @@ import ( "github.com/rivo/uniseg" "golang.org/x/text/unicode/norm" + "github.com/onflow/cadence/fixedpoint" "github.com/onflow/cadence/runtime/ast" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/common/orderedmap" @@ -15817,8 +15818,10 @@ func (v Fix64Value) Mod(interpreter *Interpreter, other NumberValue, locationRan } func (v Fix64Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { - val := new(big.Int).SetInt64(int64(v)) - return BigIntSqrt(interpreter, val, locationRange) + val := new(big.Int).SetUint64(uint64(v)) + sqrtWithFix64FactorSqrt := BigIntSqrt(interpreter, val, locationRange) + sqrt := sqrtWithFix64FactorSqrt / fixedpoint.Fix64FactorSqrt + return sqrt } func (v Fix64Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { @@ -16346,7 +16349,9 @@ func (v UFix64Value) Mod(interpreter *Interpreter, other NumberValue, locationRa func (v UFix64Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value { val := new(big.Int).SetUint64(uint64(v)) - return BigIntSqrt(interpreter, val, locationRange) + sqrtWithFix64FactorSqrt := BigIntSqrt(interpreter, val, locationRange) + sqrt := sqrtWithFix64FactorSqrt / fixedpoint.Fix64FactorSqrt + return sqrt } func (v UFix64Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue { diff --git a/runtime/math_test.go b/runtime/math_test.go new file mode 100644 index 0000000000..377c87ce01 --- /dev/null +++ b/runtime/math_test.go @@ -0,0 +1,354 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package runtime_test + +import ( + "fmt" + "math/big" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence" + "github.com/onflow/cadence/encoding/json" + . "github.com/onflow/cadence/runtime" + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/interpreter" + "github.com/onflow/cadence/runtime/sema" + . "github.com/onflow/cadence/runtime/tests/runtime_utils" +) + +// Maximum integer whose square-root can be computed and stored in a UFix64. +var maxSquareInteger *big.Int + +func init() { + // Maximum value supported by a UFix64 is 184467440737.09551615. + // So the smallest number that overflows is 184467440737.09551616. + // (184467440737.09551616)^2 = 34028236692093846346337.4607431768211456 + // + // Note that we have opted of rounding mode ToZero (IEEE 754-2008 roundTowardZero). + // So we can support Sqrt till 34028236692093846346337 since + // Sqrt(34028236692093846346337) = 184467440737.09551615999875115311 + // which gets rounded down to 184467440737.09551615 + // Sqrt(34028236692093846346338) = 184467440737.09551616000146165854 + // which gets rounded to 184467440737.09551616 which overflows. + maxSquareInteger, _ = new(big.Int).SetString("34028236692093846346337", 10) +} + +func TestRuntimeMathSqrt(t *testing.T) { + + t.Parallel() + + runtime := NewTestInterpreterRuntime() + + script := ` + + access(all) fun main(_ data: %s): UFix64 { + return Math.Sqrt(data) + } + ` + + newUFix64 := func(t *testing.T, integer int, fraction uint) cadence.UFix64 { + value, err := cadence.NewUFix64FromParts(integer, fraction) + require.NoError(t, err) + return value + } + + tests := map[string]map[cadence.NumberValue]cadence.UFix64{ + // Int* + "Int": { + cadence.NewInt(0): newUFix64(t, 0, 0), + cadence.NewInt(42): newUFix64(t, 6, 48074069), + cadence.NewInt(127): newUFix64(t, 11, 26942766), + cadence.NewIntFromBig(maxSquareInteger): newUFix64(t, 184467440737, 9551615), + }, + "Int8": { + cadence.Int8(0): newUFix64(t, 0, 0), + cadence.Int8(40): newUFix64(t, 6, 32455532), + cadence.Int8(124): newUFix64(t, 11, 13552872), + cadence.Int8(127): newUFix64(t, 11, 26942766), + }, + "Int16": { + cadence.NewInt16(0): newUFix64(t, 0, 0), + cadence.NewInt16(40): newUFix64(t, 6, 32455532), + cadence.NewInt16(32767): newUFix64(t, 181, 1657382), + cadence.NewInt16(10000): newUFix64(t, 100, 0), + }, + "Int32": { + cadence.NewInt32(0): newUFix64(t, 0, 0), + cadence.NewInt32(42): newUFix64(t, 6, 48074069), + cadence.NewInt32(10000): newUFix64(t, 100, 0), + cadence.NewInt32(2147483647): newUFix64(t, 46340, 95000105), + }, + "Int64": { + cadence.NewInt64(0): newUFix64(t, 0, 0), + cadence.NewInt64(42): newUFix64(t, 6, 48074069), + cadence.NewInt64(10000): newUFix64(t, 100, 0), + cadence.NewInt64(9223372036854775807): newUFix64(t, 3037000499, 97604969), + }, + "Int128": { + cadence.NewInt128(0): newUFix64(t, 0, 0), + cadence.NewInt128(42): newUFix64(t, 6, 48074069), + cadence.NewInt128(127): newUFix64(t, 11, 26942766), + cadence.NewInt128(128): newUFix64(t, 11, 31370849), + cadence.NewInt128(200): newUFix64(t, 14, 14213562), + cadence.NewInt128(10000): newUFix64(t, 100, 0), + cadence.Int128{Value: maxSquareInteger}: newUFix64(t, 184467440737, 9551615), + }, + "Int256": { + cadence.NewInt256(0): newUFix64(t, 0, 0), + cadence.NewInt256(42): newUFix64(t, 6, 48074069), + cadence.NewInt256(127): newUFix64(t, 11, 26942766), + cadence.NewInt256(128): newUFix64(t, 11, 31370849), + cadence.NewInt256(200): newUFix64(t, 14, 14213562), + cadence.Int256{Value: maxSquareInteger}: newUFix64(t, 184467440737, 9551615), + }, + // UInt* + "UInt": { + cadence.NewUInt(0): newUFix64(t, 0, 0), + cadence.NewUInt(42): newUFix64(t, 6, 48074069), + cadence.NewUInt(127): newUFix64(t, 11, 26942766), + cadence.UInt{Value: maxSquareInteger}: newUFix64(t, 184467440737, 9551615), + }, + "UInt8": { + cadence.UInt8(0): newUFix64(t, 0, 0), + cadence.UInt8(40): newUFix64(t, 6, 32455532), + cadence.UInt8(124): newUFix64(t, 11, 13552872), + cadence.UInt8(127): newUFix64(t, 11, 26942766), + }, + "UInt16": { + cadence.NewUInt16(0): newUFix64(t, 0, 0), + cadence.NewUInt16(40): newUFix64(t, 6, 32455532), + cadence.NewUInt16(32767): newUFix64(t, 181, 1657382), + cadence.NewUInt16(10000): newUFix64(t, 100, 0), + }, + "UInt32": { + cadence.NewUInt32(0): newUFix64(t, 0, 0), + cadence.NewUInt32(42): newUFix64(t, 6, 48074069), + cadence.NewUInt32(10000): newUFix64(t, 100, 0), + cadence.NewUInt32(2147483647): newUFix64(t, 46340, 95000105), + }, + "UInt64": { + cadence.NewUInt64(0): newUFix64(t, 0, 0), + cadence.NewUInt64(42): newUFix64(t, 6, 48074069), + cadence.NewUInt64(10000): newUFix64(t, 100, 0), + cadence.NewUInt64(9223372036854775807): newUFix64(t, 3037000499, 97604969), + }, + "UInt128": { + cadence.NewUInt128(0): newUFix64(t, 0, 0), + cadence.NewUInt128(42): newUFix64(t, 6, 48074069), + cadence.NewUInt128(127): newUFix64(t, 11, 26942766), + cadence.NewUInt128(128): newUFix64(t, 11, 31370849), + cadence.NewUInt128(200): newUFix64(t, 14, 14213562), + cadence.NewUInt128(10000): newUFix64(t, 100, 0), + cadence.UInt128{Value: maxSquareInteger}: newUFix64(t, 184467440737, 9551615), + }, + "UInt256": { + cadence.NewUInt256(0): newUFix64(t, 0, 0), + cadence.NewUInt256(42): newUFix64(t, 6, 48074069), + cadence.NewUInt256(127): newUFix64(t, 11, 26942766), + cadence.NewUInt256(128): newUFix64(t, 11, 31370849), + cadence.NewUInt256(200): newUFix64(t, 14, 14213562), + cadence.UInt256{Value: maxSquareInteger}: newUFix64(t, 184467440737, 9551615), + }, + // Word* + "Word8": { + cadence.Word8(0): newUFix64(t, 0, 0), + cadence.Word8(40): newUFix64(t, 6, 32455532), + cadence.Word8(124): newUFix64(t, 11, 13552872), + cadence.Word8(127): newUFix64(t, 11, 26942766), + }, + "Word16": { + cadence.NewWord16(0): newUFix64(t, 0, 0), + cadence.NewWord16(40): newUFix64(t, 6, 32455532), + cadence.NewWord16(32767): newUFix64(t, 181, 1657382), + cadence.NewWord16(10000): newUFix64(t, 100, 0), + }, + "Word32": { + cadence.NewWord32(0): newUFix64(t, 0, 0), + cadence.NewWord32(42): newUFix64(t, 6, 48074069), + cadence.NewWord32(10000): newUFix64(t, 100, 0), + cadence.NewWord32(2147483647): newUFix64(t, 46340, 95000105), + }, + "Word64": { + cadence.NewWord64(0): newUFix64(t, 0, 0), + cadence.NewWord64(42): newUFix64(t, 6, 48074069), + cadence.NewWord64(10000): newUFix64(t, 100, 0), + cadence.NewWord64(9223372036854775807): newUFix64(t, 3037000499, 97604969), + }, + "Word128": { + cadence.NewWord128(0): newUFix64(t, 0, 0), + cadence.NewWord128(42): newUFix64(t, 6, 48074069), + cadence.NewWord128(127): newUFix64(t, 11, 26942766), + cadence.NewWord128(128): newUFix64(t, 11, 31370849), + cadence.NewWord128(200): newUFix64(t, 14, 14213562), + cadence.NewWord128(10000): newUFix64(t, 100, 0), + cadence.Word128{Value: maxSquareInteger}: newUFix64(t, 184467440737, 9551615), + }, + "Word256": { + cadence.NewWord256(0): newUFix64(t, 0, 0), + cadence.NewWord256(42): newUFix64(t, 6, 48074069), + cadence.NewWord256(127): newUFix64(t, 11, 26942766), + cadence.NewWord256(128): newUFix64(t, 11, 31370849), + cadence.NewWord256(200): newUFix64(t, 14, 14213562), + cadence.Word256{Value: maxSquareInteger}: newUFix64(t, 184467440737, 9551615), + }, + // Fix* + "Fix64": { + cadence.Fix64(0): newUFix64(t, 0, 0), + cadence.Fix64(42_00000000): newUFix64(t, 6, 48074069), + cadence.Fix64(92233720368_54775807): newUFix64(t, 303700, 4999760), + }, + // UFix* + "UFix64": { + cadence.UFix64(0): newUFix64(t, 0, 0), + cadence.UFix64(42_00000000): newUFix64(t, 6, 48074069), + cadence.UFix64(184467440737_09551615): newUFix64(t, 429496, 72959999), + }, + } + + // Ensure the test cases are complete + + for _, numberType := range sema.AllNumberTypes { + switch numberType { + case sema.NumberType, sema.SignedNumberType, + sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType, + sema.FixedPointType, sema.SignedFixedPointType: + continue + } + + if _, ok := tests[numberType.String()]; !ok { + panic(fmt.Sprintf("broken test: missing %s", numberType)) + } + } + + test := func( + ty string, + input cadence.NumberValue, + expectedResult cadence.UFix64, + ) { + t.Run(fmt.Sprintf("Sqrt<%s>(%s)", ty, input), func(t *testing.T) { + + t.Parallel() + + runtimeInterface := &TestRuntimeInterface{ + Storage: NewTestLedger(nil, nil), + OnDecodeArgument: func(b []byte, t cadence.Type) (value cadence.Value, err error) { + return json.Decode(nil, b) + }, + } + + result, err := runtime.ExecuteScript( + Script{ + Source: []byte(fmt.Sprintf(script, ty)), + Arguments: encodeArgs([]cadence.Value{ + input, + }), + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + }, + ) + + require.NoError(t, err) + assert.Equal(t, + expectedResult, + result, + ) + }) + } + + for ty, tests := range tests { + for value, expected := range tests { + test(ty, value, expected) + } + } +} + +func TestRuntimeMathSqrtInvalid(t *testing.T) { + + t.Parallel() + + runtime := NewTestInterpreterRuntime() + + script := ` + + access(all) fun main(_ data: %s): UFix64 { + return Math.Sqrt(data) + } + ` + + type testCase struct { + name string + ty string + input cadence.NumberValue + expectedError error + } + + test := func(tc *testCase) { + t.Run(fmt.Sprintf("Sqrt<%s>(%s)", tc.ty, tc.input), func(t *testing.T) { + + t.Parallel() + + runtimeInterface := &TestRuntimeInterface{ + Storage: NewTestLedger(nil, nil), + OnDecodeArgument: func(b []byte, t cadence.Type) (value cadence.Value, err error) { + return json.Decode(nil, b) + }, + } + + _, err := runtime.ExecuteScript( + Script{ + Source: []byte(fmt.Sprintf(script, tc.ty)), + Arguments: encodeArgs([]cadence.Value{ + tc.input, + }), + }, + Context{ + Interface: runtimeInterface, + Location: common.ScriptLocation{}, + }, + ) + + require.Error(t, err) + assert.ErrorAs(t, err, tc.expectedError) + }) + } + + tests := []*testCase{ + { + name: "Overflow", + ty: "Int", + input: cadence.NewIntFromBig(new(big.Int).Add(maxSquareInteger, new(big.Int).SetInt64(1))), + expectedError: &interpreter.OverflowError{}, + }, + { + name: "Negative param underflows", + ty: "Int32", + input: cadence.NewInt32(-1), + expectedError: &interpreter.UnderflowError{}, + }, + } + + for _, tc := range tests { + test(tc) + } +} diff --git a/runtime/stdlib/builtin.go b/runtime/stdlib/builtin.go index ddaf56a5f3..d42c59a1da 100644 --- a/runtime/stdlib/builtin.go +++ b/runtime/stdlib/builtin.go @@ -44,8 +44,8 @@ func DefaultStandardLibraryValues(handler StandardLibraryHandler) []StandardLibr PanicFunction, SignatureAlgorithmConstructor, RLPContract, + MathContract, InclusiveRangeConstructorFunction, - SqrtFunction, NewLogFunction(handler), NewRevertibleRandomFunction(handler), NewGetBlockFunction(handler), diff --git a/runtime/stdlib/math.go b/runtime/stdlib/math.go index 1ed2c12fa3..1d68fb4143 100644 --- a/runtime/stdlib/math.go +++ b/runtime/stdlib/math.go @@ -19,6 +19,8 @@ package stdlib import ( + "github.com/onflow/cadence/runtime/ast" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/errors" "github.com/onflow/cadence/runtime/interpreter" "github.com/onflow/cadence/runtime/sema" @@ -26,9 +28,15 @@ import ( // SqrtFunction -const sqrtFunctionDocString = `TODO` +const mathTypeSqrtFunctionDocString = ` +Computes the square root of the value and returns it. +Available on all Number types. +Panics with error if the provided value is < 0. +` -var sqrtFunctionType = func() *sema.FunctionType { +const mathTypeSqrtFunctionName = "Sqrt" + +var mathTypeSqrtFunctionType = func() *sema.FunctionType { typeParameter := &sema.TypeParameter{ Name: "T", TypeBound: sema.NumberType, @@ -55,10 +63,8 @@ var sqrtFunctionType = func() *sema.FunctionType { } }() -var SqrtFunction = NewStandardLibraryFunction( - "Sqrt", - sqrtFunctionType, - sqrtFunctionDocString, +var mathSqrtFunction = interpreter.NewUnmeteredHostFunctionValue( + mathTypeSqrtFunctionType, func(invocation interpreter.Invocation) interpreter.Value { value, ok := invocation.Arguments[0].(interpreter.NumberValue) @@ -69,3 +75,57 @@ var SqrtFunction = NewStandardLibraryFunction( return value.Sqrt(invocation.Interpreter, invocation.LocationRange) }, ) + +// Math Contract + +const MathTypeName = "Math" + +var MathType = func() *sema.CompositeType { + var t = &sema.CompositeType{ + Identifier: MathTypeName, + Kind: common.CompositeKindContract, + ImportableBuiltin: false, + HasComputedMembers: true, + } + + return t +}() + +func init() { + var members = []*sema.Member{ + sema.NewUnmeteredFunctionMember( + MathType, + sema.PrimitiveAccess(ast.AccessAll), + mathTypeSqrtFunctionName, + mathTypeSqrtFunctionType, + mathTypeSqrtFunctionDocString, + ), + } + + MathType.Members = sema.MembersAsMap(members) + MathType.Fields = sema.MembersFieldNames(members) +} + +var mathContractFields = map[string]interpreter.Value{ + mathTypeSqrtFunctionName: mathSqrtFunction, +} + +var MathTypeStaticType = interpreter.ConvertSemaToStaticType(nil, MathType) + +var mathContractValue = interpreter.NewSimpleCompositeValue( + nil, + MathType.ID(), + MathTypeStaticType, + nil, + mathContractFields, + nil, + nil, + nil, +) + +var MathContract = StandardLibraryValue{ + Name: MathTypeName, + Type: MathType, + Value: mathContractValue, + Kind: common.DeclarationKindContract, +} diff --git a/runtime/tests/checker/math_test.go b/runtime/tests/checker/math_test.go new file mode 100644 index 0000000000..d3a48368fe --- /dev/null +++ b/runtime/tests/checker/math_test.go @@ -0,0 +1,122 @@ +/* + * Cadence - The resource-oriented smart contract programming language + * + * Copyright Dapper Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package checker + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/sema" + "github.com/onflow/cadence/runtime/stdlib" +) + +func TestCheckMathSqrt(t *testing.T) { + + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.MathContract) + + runTest := func(t *testing.T, numberType sema.Type) { + t.Run(fmt.Sprintf("Sqrt<%s>", numberType), func(t *testing.T) { + t.Parallel() + + _, err := ParseAndCheckWithOptions(t, + fmt.Sprintf(` + let l: UFix64 = Math.Sqrt(%s(1)) + `, numberType), + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivationHandler: func(_ common.Location) *sema.VariableActivation { + return baseValueActivation + }, + }, + }, + ) + + require.NoError(t, err) + }) + } + + for _, numberType := range sema.AllNumberTypes { + switch numberType { + // Test only leaf types. + case sema.NumberType, sema.SignedNumberType, + sema.IntegerType, sema.SignedIntegerType, sema.FixedSizeUnsignedIntegerType, + sema.FixedPointType, sema.SignedFixedPointType: + continue + } + + runTest(t, numberType) + } +} + +func TestCheckInvalidTypeMathSqrt(t *testing.T) { + + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.MathContract) + + _, err := ParseAndCheckWithOptions(t, + ` + let l = Math.Sqrt("string") + `, + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivationHandler: func(_ common.Location) *sema.VariableActivation { + return baseValueActivation + }, + }, + }, + ) + + errs := RequireCheckerErrors(t, err, 1) + require.IsType(t, &sema.TypeMismatchError{}, errs[0]) +} + +func TestCheckInvalidNumberArgumentsMathSqrt(t *testing.T) { + + t.Parallel() + + baseValueActivation := sema.NewVariableActivation(sema.BaseValueActivation) + baseValueActivation.DeclareValue(stdlib.MathContract) + + _, err := ParseAndCheckWithOptions(t, + ` + let x = Math.Sqrt() + let l = Math.Sqrt(1, 2) + `, + ParseAndCheckOptions{ + Config: &sema.Config{ + BaseValueActivationHandler: func(_ common.Location) *sema.VariableActivation { + return baseValueActivation + }, + }, + }, + ) + + errs := RequireCheckerErrors(t, err, 3) + require.IsType(t, &sema.InsufficientArgumentsError{}, errs[0]) + require.IsType(t, &sema.TypeParameterTypeInferenceError{}, errs[1]) + require.IsType(t, &sema.ExcessiveArgumentsError{}, errs[2]) +} From 370e29aa66a14d09fb7f3362a91d97794d08c807 Mon Sep 17 00:00:00 2001 From: darkdrag00nv2 <122124396+darkdrag00nv2@users.noreply.github.com> Date: Fri, 16 Feb 2024 20:53:46 +0530 Subject: [PATCH 3/5] Add comment for the rounding mode Co-authored-by: Alexander Hentschel --- runtime/interpreter/big.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/runtime/interpreter/big.go b/runtime/interpreter/big.go index e9731f97f7..2de40889a1 100644 --- a/runtime/interpreter/big.go +++ b/runtime/interpreter/big.go @@ -150,6 +150,15 @@ func BigIntSqrt(interpreter *Interpreter, value *big.Int, locationRange Location } valueFloat := new(big.Float).SetPrec(256).SetInt(value) + // Converting the result to a fixed-point number, we are conceptually converting it to an integer + // IEEE 754 specifies different rounding modes https: //en.wikipedia.org/wiki/IEEE_754#Rounding_rules + // We follow the "Rationale for International Standard -- Programming Languages -- C", Revision 5.10, April-2003: + // > Section 6.3.1.5 Real floating types: + // > When a finite value of real floating type is converted to an integer type other than Bool, + // > the fractional part is discarded (i.e., the value is truncated toward zero). If the value + // > of the integral part cannot be represented by the integer type, the behavior is undefined. + // For details, see + // https: //wiki.sei.cmu.edu/confluence/display/c/FLP34-C.+Ensure+that+floating-point+conversions+are+within+range+of+the+new+type res := new(big.Float).SetPrec(256).SetMode(big.ToZero).Sqrt(valueFloat) valueGetter := func() uint64 { From 2beb22354bd97873f9e156e4a86ccf40661cf185 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 16 Feb 2024 20:52:56 +0530 Subject: [PATCH 4/5] Move calculation out of valueGetter --- runtime/interpreter/big.go | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/runtime/interpreter/big.go b/runtime/interpreter/big.go index 2de40889a1..9e0ee9e0a4 100644 --- a/runtime/interpreter/big.go +++ b/runtime/interpreter/big.go @@ -160,19 +160,17 @@ func BigIntSqrt(interpreter *Interpreter, value *big.Int, locationRange Location // For details, see // https: //wiki.sei.cmu.edu/confluence/display/c/FLP34-C.+Ensure+that+floating-point+conversions+are+within+range+of+the+new+type res := new(big.Float).SetPrec(256).SetMode(big.ToZero).Sqrt(valueFloat) + res.Mul(res, new(big.Float).SetPrec(256).SetInt(sema.Fix64FactorBig)) - valueGetter := func() uint64 { - res.Mul(res, new(big.Float).SetPrec(256).SetInt(sema.Fix64FactorBig)) - - resInt := new(big.Int) - res.Int(resInt) - - if !resInt.IsUint64() { - panic(OverflowError{ - LocationRange: locationRange, - }) - } + resInt := new(big.Int) + res.Int(resInt) + if !resInt.IsUint64() { + panic(OverflowError{ + LocationRange: locationRange, + }) + } + valueGetter := func() uint64 { return resInt.Uint64() } From b2f799b13f9c4c5a2260231049534c390838f619 Mon Sep 17 00:00:00 2001 From: darkdrag00n Date: Fri, 16 Feb 2024 21:26:48 +0530 Subject: [PATCH 5/5] Add comment about 256 bit precision being sufficient --- runtime/interpreter/big.go | 24 ++++++++++++++++-------- runtime/sema/type.go | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/runtime/interpreter/big.go b/runtime/interpreter/big.go index 9e0ee9e0a4..3aaa04197a 100644 --- a/runtime/interpreter/big.go +++ b/runtime/interpreter/big.go @@ -149,25 +149,33 @@ func BigIntSqrt(interpreter *Interpreter, value *big.Int, locationRange Location }) } + if value.Cmp(sema.MaxSquareIntegerBig) == 1 { + panic(OverflowError{ + LocationRange: locationRange, + }) + } + + // Once we reach here, Cadence integer values are guaranteed to fit into + // floating-point values with 256 bit precision _without_ truncation. + // This is because of the above check with sema.MaxSquareIntegerBig. valueFloat := new(big.Float).SetPrec(256).SetInt(value) - // Converting the result to a fixed-point number, we are conceptually converting it to an integer + res := new(big.Float).SetPrec(256).SetMode(big.ToZero).Sqrt(valueFloat) + res.Mul(res, new(big.Float).SetPrec(256).SetInt(sema.Fix64FactorBig)) + + // Converting the result to a fixed-point number, we are conceptually converting it to an integer // IEEE 754 specifies different rounding modes https: //en.wikipedia.org/wiki/IEEE_754#Rounding_rules // We follow the "Rationale for International Standard -- Programming Languages -- C", Revision 5.10, April-2003: // > Section 6.3.1.5 Real floating types: - // > When a finite value of real floating type is converted to an integer type other than Bool, + // > When a finite value of real floating type is converted to an integer type other than Bool, // > the fractional part is discarded (i.e., the value is truncated toward zero). If the value // > of the integral part cannot be represented by the integer type, the behavior is undefined. // For details, see // https: //wiki.sei.cmu.edu/confluence/display/c/FLP34-C.+Ensure+that+floating-point+conversions+are+within+range+of+the+new+type - res := new(big.Float).SetPrec(256).SetMode(big.ToZero).Sqrt(valueFloat) - res.Mul(res, new(big.Float).SetPrec(256).SetInt(sema.Fix64FactorBig)) - resInt := new(big.Int) res.Int(resInt) if !resInt.IsUint64() { - panic(OverflowError{ - LocationRange: locationRange, - }) + // We checked for overflow above, so we shouldn't hit this. + panic(errors.NewUnreachableError()) } valueGetter := func() uint64 { diff --git a/runtime/sema/type.go b/runtime/sema/type.go index fb1c21ccc7..8c0f1a8498 100644 --- a/runtime/sema/type.go +++ b/runtime/sema/type.go @@ -2027,6 +2027,20 @@ var ( UFix64TypeMinFractionalBig = fixedpoint.UFix64TypeMinFractionalBig UFix64TypeMaxFractionalBig = fixedpoint.UFix64TypeMaxFractionalBig + + // Represents the maximum value whose Sqrt can be calcuated and represented in a UFix64. + // + // Maximum value supported by a UFix64 is 184467440737.09551615. + // So the smallest number that overflows is 184467440737.09551616. + // (184467440737.09551616)^2 = 34028236692093846346337.4607431768211456 + // + // We have opted of rounding mode ToZero (IEEE 754-2008 roundTowardZero). + // So we can support Sqrt till 34028236692093846346337 since + // Sqrt(34028236692093846346337) = 184467440737.09551615999875115311 + // which gets rounded down to 184467440737.09551615 + // Sqrt(34028236692093846346338) = 184467440737.09551616000146165854 + // which gets rounded to 184467440737.09551616 which overflows. + MaxSquareIntegerBig, _ = new(big.Int).SetString("34028236692093846346337", 10) ) // size constants (in bytes) for fixed-width numeric types