From 933b2cc9e3854dc9746a6cbcefca8812b75f9c28 Mon Sep 17 00:00:00 2001 From: Alexander Chepurnoy Date: Sat, 2 Nov 2024 02:07:22 +0300 Subject: [PATCH] improving UnsignedBigInt support in JS --- core/js/src/main/scala/sigma/js/Type.scala | 3 ++ .../src/main/scala/sigma/data/CBigInt.scala | 5 +-- .../shared/src/main/scala/sigma/package.scala | 2 +- .../sigma/reflection/ReflectionData.scala | 35 ++++++++++--------- data/js/src/main/scala/sigma/js/Value.scala | 2 ++ .../src/main/scala/sigma/ast/methods.scala | 5 ++- .../scala/sigma/data/CSigmaDslBuilder.scala | 9 +++-- .../scala/sigma/data/DataValueComparer.scala | 1 + .../scala/sigma/serialization/OpCodes.scala | 1 + .../org/ergoplatform/dsl/ContractSyntax.scala | 1 + .../sigma/compiler/ir/GraphIRReflection.scala | 6 ++-- .../ir/wrappers/sigma/impl/SigmaDslImpl.scala | 3 +- .../test/scala/sigma/SigmaDslTesting.scala | 3 +- .../utxo/BasicOpsSpecification.scala | 1 - .../org/ergoplatform/sdk/JavaHelpers.scala | 10 ------ .../org/ergoplatform/sdk/utils/Zero.scala | 5 ++- 16 files changed, 49 insertions(+), 43 deletions(-) diff --git a/core/js/src/main/scala/sigma/js/Type.scala b/core/js/src/main/scala/sigma/js/Type.scala index b323273a0c..069cca3e1e 100644 --- a/core/js/src/main/scala/sigma/js/Type.scala +++ b/core/js/src/main/scala/sigma/js/Type.scala @@ -35,6 +35,9 @@ object Type extends js.Object { /** Descriptor of ErgoScript type BigInt. */ val BigInt = new Type(sigma.BigIntRType) + /** Descriptor of ErgoScript type BigInt. */ + val UnsignedBigInt = new Type(sigma.UnsignedBigIntRType) + /** Descriptor of ErgoScript type GroupElement. */ val GroupElement = new Type(sigma.GroupElementRType) diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala index 8212955103..3787e78a86 100644 --- a/core/shared/src/main/scala/sigma/data/CBigInt.scala +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -69,9 +69,9 @@ case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with Wr } } -/** A default implementation of [[BigInt]] interface. +/** A default implementation of [[UnsignedBigInt]] interface. * - * @see [[BigInt]] for detailed descriptions + * @see [[UnsignedBigInt]] for detailed descriptions */ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends UnsignedBigInt with WrapperOf[BigInteger] { @@ -144,4 +144,5 @@ case class CUnsignedBigInt(override val wrappedValue: BigInteger) extends Unsign override def toSigned(): BigInt = { CBigInt(wrappedValue.toSignedBigIntValueExact) } + } diff --git a/core/shared/src/main/scala/sigma/package.scala b/core/shared/src/main/scala/sigma/package.scala index 4473bd338f..41f90b33bb 100644 --- a/core/shared/src/main/scala/sigma/package.scala +++ b/core/shared/src/main/scala/sigma/package.scala @@ -26,7 +26,7 @@ package object sigma { implicit val StringType : RType[String] = GeneralType(StringClassTag) implicit val BigIntRType : RType[BigInt] = GeneralType(BigIntClassTag) - implicit val UnsignedBigIntRType : RType[UnsignedBigInt] = GeneralType(UnsignedBigIntClassTag) + implicit val UnsignedBigIntRType : RType[UnsignedBigInt] = GeneralType(UnsignedBigIntClassTag) implicit val GroupElementRType: RType[GroupElement] = GeneralType(GroupElementClassTag) implicit val SigmaPropRType : RType[SigmaProp] = GeneralType(SigmaPropClassTag) implicit val SigmaBooleanRType: RType[SigmaBoolean] = GeneralType(SigmaBooleanClassTag) diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index ef8e52e6cd..204792ee50 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -130,44 +130,45 @@ object ReflectionData { } { val clazz = classOf[sigma.UnsignedBigInt] - val paramTypes = Array[Class[_]](clazz) + val oneParamTypes = Array[Class[_]](clazz) + val twoParamTypes = Array[Class[_]](clazz, clazz) registerClassEntry(clazz, methods = Map( - mkMethod(clazz, "add", paramTypes) { (obj, args) => + mkMethod(clazz, "add", oneParamTypes) { (obj, args) => obj.asInstanceOf[UnsignedBigInt].add(args(0).asInstanceOf[UnsignedBigInt]) }, - mkMethod(clazz, "max", paramTypes) { (obj, args) => + mkMethod(clazz, "max", oneParamTypes) { (obj, args) => obj.asInstanceOf[UnsignedBigInt].max(args(0).asInstanceOf[UnsignedBigInt]) }, - mkMethod(clazz, "min", paramTypes) { (obj, args) => + mkMethod(clazz, "min", oneParamTypes) { (obj, args) => obj.asInstanceOf[UnsignedBigInt].min(args(0).asInstanceOf[UnsignedBigInt]) }, - mkMethod(clazz, "subtract", paramTypes) { (obj, args) => + mkMethod(clazz, "subtract", oneParamTypes) { (obj, args) => obj.asInstanceOf[UnsignedBigInt].subtract(args(0).asInstanceOf[UnsignedBigInt]) }, - mkMethod(clazz, "multiply", paramTypes) { (obj, args) => + mkMethod(clazz, "multiply", oneParamTypes) { (obj, args) => obj.asInstanceOf[UnsignedBigInt].multiply(args(0).asInstanceOf[UnsignedBigInt]) }, - mkMethod(clazz, "mod", paramTypes) { (obj, args) => + mkMethod(clazz, "mod", oneParamTypes) { (obj, args) => obj.asInstanceOf[UnsignedBigInt].mod(args(0).asInstanceOf[UnsignedBigInt]) }, - mkMethod(clazz, "divide", paramTypes) { (obj, args) => + mkMethod(clazz, "divide", oneParamTypes) { (obj, args) => obj.asInstanceOf[UnsignedBigInt].divide(args(0).asInstanceOf[UnsignedBigInt]) }, - mkMethod(clazz, "plusMod", paramTypes) { (obj, args) => + mkMethod(clazz, "mod", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].mod(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "modInverse", oneParamTypes) { (obj, args) => + obj.asInstanceOf[UnsignedBigInt].modInverse(args(0).asInstanceOf[UnsignedBigInt]) + }, + mkMethod(clazz, "plusMod", twoParamTypes) { (obj, args) => obj.asInstanceOf[UnsignedBigInt].plusMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt]) }, - mkMethod(clazz, "subtractMod", paramTypes) { (obj, args) => + mkMethod(clazz, "subtractMod", twoParamTypes) { (obj, args) => obj.asInstanceOf[UnsignedBigInt].subtractMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt]) }, - mkMethod(clazz, "multiplyMod", paramTypes) { (obj, args) => + mkMethod(clazz, "multiplyMod", twoParamTypes) { (obj, args) => obj.asInstanceOf[UnsignedBigInt].multiplyMod(args(0).asInstanceOf[UnsignedBigInt], args(1).asInstanceOf[UnsignedBigInt]) - }, - mkMethod(clazz, "mod", paramTypes) { (obj, args) => - obj.asInstanceOf[UnsignedBigInt].mod(args(0).asInstanceOf[UnsignedBigInt]) - }, - mkMethod(clazz, "modInverse", paramTypes) { (obj, args) => - obj.asInstanceOf[UnsignedBigInt].modInverse(args(0).asInstanceOf[UnsignedBigInt]) } ) ) diff --git a/data/js/src/main/scala/sigma/js/Value.scala b/data/js/src/main/scala/sigma/js/Value.scala index 77d6c8e104..1fedb30250 100644 --- a/data/js/src/main/scala/sigma/js/Value.scala +++ b/data/js/src/main/scala/sigma/js/Value.scala @@ -164,6 +164,8 @@ object Value extends js.Object { n case sigma.BigIntRType => data.asInstanceOf[js.BigInt] + case sigma.UnsignedBigIntRType => + data.asInstanceOf[js.BigInt] case sigma.GroupElementRType => data.asInstanceOf[GroupElement] case sigma.SigmaPropRType => diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 73e21b4564..b64abfde1f 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -3,7 +3,7 @@ package sigma.ast import org.ergoplatform._ import org.ergoplatform.validation._ import sigma.Evaluation.stypeToRType -import sigma._ +import sigma.{UnsignedBigInt, _} import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} import sigma.ast.SType.TypeCode @@ -536,7 +536,6 @@ case object SUnsignedBigIntMethods extends SNumericTypeMethods { final val ToNBitsCostInfo = OperationCostInfo( FixedCost(JitCost(5)), NamedDesc("NBitsMethodCall")) - // todo: costing final val ModInverseCostInfo = ToNBitsCostInfo @@ -1008,7 +1007,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { | \lst{f} to each element of this collection and concatenating the results. """.stripMargin, ArgInfo("f", "the function to apply to each element.")) - /** We assume all flatMap body patterns have similar executon cost. */ + /** We assume all flatMap body patterns have similar execution cost. */ final val CheckFlatmapBody_Info = OperationCostInfo( PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(20), chunkSize = 1), NamedDesc("CheckFlatmapBody")) diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index aebc95dd00..661cd183a1 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -5,9 +5,8 @@ import org.ergoplatform.ErgoBox import org.ergoplatform.validation.ValidationRules import scorex.crypto.hash.{Blake2b256, Sha256} import scorex.utils.{Ints, Longs} -import sigma.ast.{AtLeast, SBigInt, SubstConstants} +import sigma.ast.{AtLeast, SBigInt, SType, SUnsignedBigInt, SubstConstants} import scorex.utils.Longs -import sigma.ast.{AtLeast, SType, SubstConstants} import sigma.crypto.{CryptoConstants, EcPointType, Ecp} import sigma.eval.Extensions.EvalCollOps import sigma.serialization.{DataSerializer, GroupElementSerializer, SigmaSerializer} @@ -235,7 +234,11 @@ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes") } CBigInt(new BigInteger(bytes.toArray).toSignedBigIntValueExact).asInstanceOf[T] - // todo: UnsignedBitInt + case sigma.UnsignedBigIntRType => + if (bytes.length > SUnsignedBigInt.MaxSizeInBytes) { + throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes in fromBigEndianBytes") + } + CUnsignedBigInt(new BigInteger(bytes.toArray).toSignedBigIntValueExact).asInstanceOf[T] case _ => throw new IllegalArgumentException("Unsupported type provided in fromBigEndianBytes") } } diff --git a/data/shared/src/main/scala/sigma/data/DataValueComparer.scala b/data/shared/src/main/scala/sigma/data/DataValueComparer.scala index e525b8f074..d44f3f8d68 100644 --- a/data/shared/src/main/scala/sigma/data/DataValueComparer.scala +++ b/data/shared/src/main/scala/sigma/data/DataValueComparer.scala @@ -139,6 +139,7 @@ object DataValueComparer { val descriptors: AVHashMap[RType[_], (OperationCostInfo[FixedCost], OperationCostInfo[PerItemCost])] = AVHashMap.fromSeq(Array[(RType[_], (OperationCostInfo[FixedCost], OperationCostInfo[PerItemCost]))]( (BigIntRType, (EQ_BigInt, EQ_COA_BigInt)), + (UnsignedBigIntRType, (EQ_BigInt, EQ_COA_BigInt)), (GroupElementRType, (EQ_GroupElement, EQ_COA_GroupElement)), (AvlTreeRType, (EQ_AvlTree, EQ_COA_AvlTree)), (BoxRType, (EQ_Box, EQ_COA_Box)), diff --git a/data/shared/src/main/scala/sigma/serialization/OpCodes.scala b/data/shared/src/main/scala/sigma/serialization/OpCodes.scala index 70050d00ba..c4647669fa 100644 --- a/data/shared/src/main/scala/sigma/serialization/OpCodes.scala +++ b/data/shared/src/main/scala/sigma/serialization/OpCodes.scala @@ -153,6 +153,7 @@ object OpCodes { val OptionIsDefinedCode: OpCode = newOpCode(118) // Modular arithmetic operations codes + // todo: remove? val ModQCode : OpCode = newOpCode(119) val PlusModQCode : OpCode = newOpCode(120) val MinusModQCode: OpCode = newOpCode(121) diff --git a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala index 11cbaff739..2554489340 100644 --- a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala +++ b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala @@ -55,6 +55,7 @@ trait ContractSyntax { contract: SigmaContract => case _: String => StringType case _: Unit => UnitType case _: sigma.BigInt => BigIntRType + case _: sigma.BigInt => UnsignedBigIntRType case _: GroupElement => GroupElementRType case _: ErgoBox => syntax.ErgoBoxRType // TODO remove this RType case _: Box => BoxRType diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala index a161a034f5..78e1c09ffa 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala @@ -145,13 +145,13 @@ object GraphIRReflection { mkMethod(clazz, "divide", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.UnsignedBigInt].divide(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]]) }, - mkMethod(clazz, "plusMod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + mkMethod(clazz, "plusMod", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.UnsignedBigInt].plusMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]]) }, - mkMethod(clazz, "subtractMod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + mkMethod(clazz, "subtractMod", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.UnsignedBigInt].subtractMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]]) }, - mkMethod(clazz, "multiplyMod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => + mkMethod(clazz, "multiplyMod", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.UnsignedBigInt].multiplyMod(args(0).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]], args(1).asInstanceOf[ctx.Ref[ctx.UnsignedBigInt]]) }, mkMethod(clazz, "mod", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => diff --git a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala index f82132d9a0..e0376b4c91 100644 --- a/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/sigma/compiler/ir/wrappers/sigma/impl/SigmaDslImpl.scala @@ -521,7 +521,8 @@ object UnsignedBigInt extends EntityObject("UnsignedBigInt") { override protected def collectMethods: Map[RMethod, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(RClass(classOf[UnsignedBigInt]), RClass(classOf[UnsignedBigInt]), Set( - "add", "subtract", "multiply", "divide", "mod", "min", "max", "plusMod", "subtractMod", "multiplyMod" + "add", "subtract", "multiply", "divide", "mod", "modInverse", + "min", "max", "plusMod", "subtractMod", "multiplyMod" )) } } diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index de9e080862..7f3f28b791 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -1376,6 +1376,7 @@ class SigmaDslTesting extends AnyPropSpec case IntType => arbInt case LongType => arbLong case BigIntRType => arbBigInt + case UnsignedBigIntRType => arbUnsignedBigInt case GroupElementRType => arbGroupElement case SigmaPropRType => arbSigmaProp case BoxRType => arbBox @@ -1404,7 +1405,7 @@ class SigmaDslTesting extends AnyPropSpec */ def updateArbitrary[A](t: RType[A], sampled: Sampled[A]) = { t match { - case BigIntRType | GroupElementRType | SigmaPropRType | + case BigIntRType | UnsignedBigIntRType | GroupElementRType | SigmaPropRType | BoxRType | PreHeaderRType | HeaderRType | AvlTreeRType | _: CollType[_] | _: PairType[_,_] | _: OptionType[_] => val newArb = Arbitrary(Gen.oneOf(sampled.samples)) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 11d5e9b00a..60dac9c16a 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -154,7 +154,6 @@ class BasicOpsSpecification extends CompilerTestingCommons flexVerifier.verify(verifyEnv, tree, ctxExt, pr.proof, fakeMessage).get._1 shouldBe true } - property("getVarFromInput") { def getVarTest(): Assertion = { val customExt = Map( diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala index 14cdf7f6bb..a46e7490db 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala @@ -315,16 +315,6 @@ object JavaHelpers { def collRType[T](tItem: RType[T]): RType[Coll[T]] = sigma.collRType(tItem) - def BigIntRType: RType[sigma.BigInt] = sigma.BigIntRType - - def GroupElementRType: RType[sigma.GroupElement] = sigma.GroupElementRType - - def SigmaPropRType: RType[sigma.SigmaProp] = sigma.SigmaPropRType - - def AvlTreeRType: RType[sigma.AvlTree] = sigma.AvlTreeRType - - def BoxRType: RType[sigma.Box] = sigma.BoxRType - def SigmaDsl: CSigmaDslBuilder = sigma.eval.SigmaDsl def collFrom(arr: Array[Byte]): Coll[Byte] = { diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala index 78dfe2dc94..a337ad44a8 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala @@ -1,7 +1,7 @@ package org.ergoplatform.sdk.utils import org.ergoplatform.ErgoBox -import sigma.data.{AvlTreeData, AvlTreeFlags, CAvlTree, CBigInt, CGroupElement, CSigmaProp, CollType, FuncType, OptionType, PairType, RType, TrivialProp, TupleType} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAvlTree, CBigInt, CGroupElement, CSigmaProp, CUnsignedBigInt, CollType, FuncType, OptionType, PairType, RType, TrivialProp, TupleType} import sigma.data.RType._ import scorex.crypto.authds.avltree.batch.BatchAVLProver import scorex.crypto.hash.{Blake2b256, Digest32} @@ -11,6 +11,7 @@ import sigma._ import sigma.ast.ErgoTree import ErgoTree.HeaderType import sigma.crypto.CryptoConstants + import java.math.BigInteger import scala.language.implicitConversions @@ -48,6 +49,7 @@ object Zero extends ZeroLowPriority { implicit val IntIsZero: Zero[Int] = CZero(0) implicit val LongIsZero: Zero[Long] = CZero(0L) implicit val BigIntIsZero: Zero[BigInt] = CZero(CBigInt(BigInteger.ZERO)) + implicit val UnsignedBigIntIsZero: Zero[UnsignedBigInt] = CZero(CUnsignedBigInt(BigInteger.ZERO)) implicit val GroupElementIsZero: Zero[GroupElement] = CZero(CGroupElement(CryptoConstants.dlogGroup.identity)) implicit val AvlTreeIsZero: Zero[AvlTree] = CZero({ val avlProver = new BatchAVLProver[Digest32, Blake2b256.type](keyLength = 32, None) @@ -88,6 +90,7 @@ object Zero extends ZeroLowPriority { case LongType => Zero[Long] case UnitType => Zero[Unit] case BigIntRType => Zero[BigInt] + case UnsignedBigIntRType => Zero[UnsignedBigInt] case BoxRType => Zero[Box] case GroupElementRType => Zero[GroupElement] case AvlTreeRType => Zero[AvlTree]