Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Sqrt on NumberType #3055

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
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
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)
darkdrag00nv2 marked this conversation as resolved.
Show resolved Hide resolved
res := new(big.Float).SetPrec(256).SetMode(big.ToZero).Sqrt(valueFloat)
darkdrag00nv2 marked this conversation as resolved.
Show resolved Hide resolved

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

resInt := new(big.Int)
res.Int(resInt)
darkdrag00nv2 marked this conversation as resolved.
Show resolved Hide resolved

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

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 @@
"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 @@
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 @@
return v.Div(interpreter, other, locationRange)
}

func (v IntValue) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {
val := v.ToBigInt(interpreter)
return BigIntSqrt(interpreter, val, locationRange)
}

Check warning on line 3849 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L3849

Added line #L3849 was not covered by tests

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 @@
return NewInt8Value(interpreter, valueGetter)
}

func (v Int8Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {

Check warning on line 4508 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L4508

Added line #L4508 was not covered by tests
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 @@
return NewInt16Value(interpreter, valueGetter)
}

func (v Int16Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {

Check warning on line 5154 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L5154

Added line #L5154 was not covered by tests
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 @@
return NewInt32Value(interpreter, valueGetter)
}

func (v Int32Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {

Check warning on line 5803 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L5803

Added line #L5803 was not covered by tests
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 @@
return NewInt64Value(interpreter, valueGetter)
}

func (v Int64Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {

Check warning on line 6451 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L6451

Added line #L6451 was not covered by tests
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 @@
return NewInt128ValueFromBigInt(interpreter, valueGetter)
}

func (v Int128Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {

Check warning on line 7154 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L7154

Added line #L7154 was not covered by tests
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 @@
return NewInt256ValueFromBigInt(interpreter, valueGetter)
}

func (v Int256Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {

Check warning on line 7901 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L7901

Added line #L7901 was not covered by tests
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 @@
return v.Div(interpreter, other, locationRange)
}

func (v UIntValue) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {
val := v.ToBigInt(interpreter)
return BigIntSqrt(interpreter, val, locationRange)
}

Check warning on line 8561 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L8561

Added line #L8561 was not covered by tests

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 @@
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)

Check warning on line 9130 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L9130

Added line #L9130 was not covered by tests
}

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 @@
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)

Check warning on line 9727 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L9727

Added line #L9727 was not covered by tests
}

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 @@
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)

Check warning on line 10275 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L10275

Added line #L10275 was not covered by tests
}

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 @@
return v.Div(interpreter, other, locationRange)
}

func (v UInt64Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {
val := v.ToBigInt(interpreter)
return BigIntSqrt(interpreter, val, locationRange)
}

Check warning on line 10852 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L10852

Added line #L10852 was not covered by tests

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 @@
return v.Div(interpreter, other, locationRange)
}

func (v UInt128Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {
val := v.ToBigInt(interpreter)
return BigIntSqrt(interpreter, val, locationRange)
}

Check warning on line 11475 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L11475

Added line #L11475 was not covered by tests

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 @@
return v.Div(interpreter, other, locationRange)
}

func (v UInt256Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {
val := v.ToBigInt(interpreter)
return BigIntSqrt(interpreter, val, locationRange)
}

Check warning on line 12157 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L12157

Added line #L12157 was not covered by tests

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 @@
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)

Check warning on line 12666 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L12665-L12666

Added lines #L12665 - L12666 were not covered by tests
}

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 @@
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)

Check warning on line 13108 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L13107-L13108

Added lines #L13107 - L13108 were not covered by tests
}

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 @@
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)

Check warning on line 13553 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L13552-L13553

Added lines #L13552 - L13553 were not covered by tests
}

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 @@
panic(errors.NewUnreachableError())
}

func (v Word64Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {
val := v.ToBigInt(interpreter)
return BigIntSqrt(interpreter, val, locationRange)
}

Check warning on line 14024 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L14023-L14024

Added lines #L14023 - L14024 were not covered by tests

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 @@
panic(errors.NewUnreachableError())
}

func (v Word128Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {
val := v.ToBigInt(interpreter)
return BigIntSqrt(interpreter, val, locationRange)
}

Check warning on line 14546 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L14545-L14546

Added lines #L14545 - L14546 were not covered by tests

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 @@
panic(errors.NewUnreachableError())
}

func (v Word256Value) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {
val := v.ToBigInt(interpreter)
return BigIntSqrt(interpreter, val, locationRange)
}

Check warning on line 15131 in runtime/interpreter/value.go

View check run for this annotation

Codecov / codecov/patch

runtime/interpreter/value.go#L15130-L15131

Added lines #L15130 - L15131 were not covered by tests

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) Sqrt(interpreter *Interpreter, locationRange LocationRange) UFix64Value {
val := new(big.Int).SetUint64(uint64(v))
sqrtWithFix64FactorSqrt := BigIntSqrt(interpreter, val, locationRange)
sqrt := sqrtWithFix64FactorSqrt / fixedpoint.Fix64FactorSqrt
Copy link
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) 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
Loading