From aaaa68a8fbabff56b25f6283d3af67f4931a6f92 Mon Sep 17 00:00:00 2001 From: ross-weir <29697678+ross-weir@users.noreply.github.com> Date: Wed, 8 Nov 2023 14:12:28 +1100 Subject: [PATCH] add Boolean.toByte operation --- .../main/scala/scalan/primitives/LogicalOps.scala | 6 ++++++ .../shared/src/main/scala/sigmastate/types.scala | 14 +++++++------- .../main/scala/sigmastate/eval/GraphBuilding.scala | 4 ++++ .../scala/sigmastate/ErgoTreeSpecification.scala | 5 ++++- .../TestingInterpreterSpecification.scala | 8 ++++++++ 5 files changed, 29 insertions(+), 8 deletions(-) diff --git a/graph-ir/shared/src/main/scala/scalan/primitives/LogicalOps.scala b/graph-ir/shared/src/main/scala/scalan/primitives/LogicalOps.scala index e81b546139..ffc91ed14e 100644 --- a/graph-ir/shared/src/main/scala/scalan/primitives/LogicalOps.scala +++ b/graph-ir/shared/src/main/scala/scalan/primitives/LogicalOps.scala @@ -29,6 +29,11 @@ trait LogicalOps extends Base { self: Scalan => override def applySeq(x: Boolean): Int = if (x) 1 else 0 } + /** Boolean to Byte conversion unary operation. */ + val BooleanToByte = new UnOp[Boolean, Byte]("ToByte") { + override def applySeq(x: Boolean): Byte = if (x) 1.toByte else 0.toByte + } + /** Extension methods over `Ref[Boolean]`. */ implicit class RepBooleanOps(value: Ref[Boolean]) { def &&(y: Ref[Boolean]): Ref[Boolean] = And(value, y) @@ -40,6 +45,7 @@ trait LogicalOps extends Base { self: Scalan => def unary_!() : Ref[Boolean] = Not(value) def toInt: Ref[Int] = BooleanToInt(value) + def toByte: Ref[Byte] = BooleanToByte(value) } diff --git a/interpreter/shared/src/main/scala/sigmastate/types.scala b/interpreter/shared/src/main/scala/sigmastate/types.scala index e4fd826304..7a600ded7f 100644 --- a/interpreter/shared/src/main/scala/sigmastate/types.scala +++ b/interpreter/shared/src/main/scala/sigmastate/types.scala @@ -238,6 +238,7 @@ object SType { } implicit class STypeOps(val tpe: SType) extends AnyVal { + def isBoolean: Boolean = tpe.isInstanceOf[SBoolean.type] def isCollectionLike: Boolean = tpe.isInstanceOf[SCollection[_]] def isCollection: Boolean = tpe.isInstanceOf[SCollectionType[_]] def isOption: Boolean = tpe.isInstanceOf[SOption[_]] @@ -918,13 +919,12 @@ case object SBoolean extends SPrimType with SEmbeddable with SLogical with SProd override val reprClass: RClass[_] = RClass(classOf[Boolean]) val ToByte = "toByte" - protected override def getMethods() = super.getMethods() - /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - ++ Seq( - SMethod(this, ToByte, SFunc(this, SByte), 1) - .withInfo(PropertyCall, "Convert true to 1 and false to 0"), - ) - */ + + lazy val ToByteMethod: SMethod = SMethod( + this, ToByte, SFunc(this, SByte), 1, FixedCost(JitCost(1))) + .withInfo(PropertyCall, "Convert true to 1 and false to 0") + + protected override def getMethods() = super.getMethods() ++ Seq(ToByteMethod) } /** Descriptor of ErgoTree type `Byte` - 8-bit signed integer. */ diff --git a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala b/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala index a992dbf0d7..8011f199f1 100644 --- a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala @@ -507,6 +507,10 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => else error(s"The type of $obj is expected to be Collection to select 'size' property", obj.sourceContext.toOption) + case Select(obj, SBoolean.ToByte, _) if obj.tpe.isBoolean => + val bool = eval(obj.asBoolValue) + bool.toByte + // Rule: proof.isProven --> IsValid(proof) case Select(p, SSigmaProp.IsProven, _) if p.tpe == SSigmaProp => eval(SigmaPropIsProven(p.asSigmaProp)) diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 7bffa895f8..b4b5aa6c57 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -218,7 +218,6 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { // The following table should be made dependent on HF activation val methods = Table( ("typeId", "methods", "CanHaveMethods"), - (SBoolean.typeId, Seq.empty[MInfo], true), (SByte.typeId, Seq.empty[MInfo], false), (SShort.typeId, Seq.empty[MInfo], false), (SInt.typeId, Seq.empty[MInfo], false), @@ -232,6 +231,10 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { ), true) }, + { + import SBoolean._ + (SBoolean.typeId, Seq(MInfo(1, ToByteMethod)), true) + }, { // SBigInt inherit methods from SNumericType.methods // however they are not resolvable via SBigInt.typeId import SNumericType._ diff --git a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala index 51e6fd9556..3557e33429 100644 --- a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala @@ -202,6 +202,14 @@ class TestingInterpreterSpecification extends CompilerTestingCommons |}""".stripMargin) } + property("Evaluate boolean casting ops") { + testEval( + """{ + | val bool: Boolean = true + | bool.toByte == 1.toByte && false.toByte == 0.toByte + }""".stripMargin) + } + property("Evaluate numeric casting ops") { def testWithCasting(castSuffix: String): Unit = { testEval(s"OUTPUTS.size.toByte.$castSuffix == 0.$castSuffix")