Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fixedpoint/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

const Fix64Scale = 8
const Fix64Factor = 100_000_000
const Fix64FactorSqrt = 10_000

// Fix64

Expand Down
29 changes: 29 additions & 0 deletions runtime/interpreter/big.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike the overflow case where the result can't fit into the target type, this case corresponds to an "undefined" case rather than an underflow. The square root being non defined for negative inputs (similar to the division by zero error where dividing by zero isn't mathematically defined).

There doesn't seem to be an "undefined" error available so I see why underflow was used, but I wanted to mention this.

LocationRange: locationRange,
})
}

valueFloat := new(big.Float).SetPrec(256).SetInt(value)
Comment thread
darkdrag00nv2 marked this conversation as resolved.
res := new(big.Float).SetPrec(256).SetMode(big.ToZero).Sqrt(valueFloat)
Comment thread
darkdrag00nv2 marked this conversation as resolved.

valueGetter := func() uint64 {
res.Mul(res, new(big.Float).SetPrec(256).SetInt(sema.Fix64FactorBig))

resInt := new(big.Int)
res.Int(resInt)
Comment thread
darkdrag00nv2 marked this conversation as resolved.
Outdated

if !resInt.IsUint64() {
panic(OverflowError{
LocationRange: locationRange,
})
}
Comment thread
darkdrag00nv2 marked this conversation as resolved.
Outdated

return resInt.Uint64()
}

return NewUFix64Value(interpreter, valueGetter)
}
126 changes: 126 additions & 0 deletions runtime/interpreter/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -3405,6 +3406,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 {
Expand Down Expand Up @@ -3841,6 +3843,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 {
Expand Down Expand Up @@ -4498,6 +4505,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 {
Expand Down Expand Up @@ -5138,6 +5151,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 {
Expand Down Expand Up @@ -5781,6 +5800,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 {
Expand Down Expand Up @@ -6423,6 +6448,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 {
Expand Down Expand Up @@ -7120,6 +7151,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 {
Expand Down Expand Up @@ -7862,6 +7898,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 {
Expand Down Expand Up @@ -8514,6 +8555,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 {
Expand Down Expand Up @@ -9078,6 +9124,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 {
Expand Down Expand Up @@ -9669,6 +9721,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 {
Expand Down Expand Up @@ -10211,6 +10269,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 {
Expand Down Expand Up @@ -10782,6 +10846,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 {
Expand Down Expand Up @@ -11400,6 +11469,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 {
Expand Down Expand Up @@ -12077,6 +12151,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 {
Expand Down Expand Up @@ -12581,6 +12660,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 {
Expand Down Expand Up @@ -13017,6 +13102,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 {
Expand Down Expand Up @@ -13456,6 +13547,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 {
Expand Down Expand Up @@ -13921,6 +14018,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 {
Expand Down Expand Up @@ -14438,6 +14540,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 {
Expand Down Expand Up @@ -15018,6 +15125,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 {
Expand Down Expand Up @@ -15705,6 +15817,13 @@ func (v Fix64Value) Mod(interpreter *Interpreter, other NumberValue, locationRan
)
}

func (v Fix64Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {
val := new(big.Int).SetUint64(uint64(v))
sqrtWithFix64FactorSqrt := BigIntSqrt(interpreter, val, locationRange)
sqrt := sqrtWithFix64FactorSqrt / fixedpoint.Fix64FactorSqrt
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible optimization: I think we can save this division if in the function BigIntSqrt (L163), we multiply by Fix64FactorSqrt instead of Fix64Factor, only in the case of Fix64Value and UFix64Value. For the rest of the types, we can keep multiplying by Fix64Factor.

return sqrt
}

func (v Fix64Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue {
o, ok := other.(Fix64Value)
if !ok {
Expand Down Expand Up @@ -16228,6 +16347,13 @@ 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))
sqrtWithFix64FactorSqrt := BigIntSqrt(interpreter, val, locationRange)
sqrt := sqrtWithFix64FactorSqrt / fixedpoint.Fix64FactorSqrt
return sqrt
}

func (v UFix64Value) Less(interpreter *Interpreter, other ComparableValue, locationRange LocationRange) BoolValue {
o, ok := other.(UFix64Value)
if !ok {
Expand Down
Loading