Skip to content

Commit

Permalink
merging w. 6.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
kushti committed Oct 7, 2024
2 parents e052255 + 83ba4a4 commit 11d869f
Show file tree
Hide file tree
Showing 13 changed files with 581 additions and 25 deletions.
25 changes: 25 additions & 0 deletions core/shared/src/main/scala/sigma/Colls.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@ trait Coll[@specialized A] {
*/
def apply(i: Int): A

/** The element at given index or None if there is no such element. Indices start at `0`.
*
* @param i the index
* @return the element at the given index, or None if there is no such element
*/
def get(i: Int): Option[A] = {
if (isDefinedAt(i)) {
Some(apply(i))
} else {
None
}
}

/** Tests whether this $coll contains given index.
*
* The implementations of methods `apply` and `isDefinedAt` turn a `Coll[A]` into
Expand Down Expand Up @@ -76,6 +89,18 @@ trait Coll[@specialized A] {
* produces a collection ((x0, y0), ..., (xK, yK)) where K = min(N, M) */
def zip[@specialized B](ys: Coll[B]): Coll[(A, B)]

/**
* @return true if first elements of this collection form given `ys` collection, false otherwise.
* E.g. [1,2,3] starts with [1,2]
*/
def startsWith(ys: Coll[A]): Boolean

/**
* @return true if last elements of this collection form given `ys` collection, false otherwise.
* E.g. [1,2,3] ends with [2,3]
*/
def endsWith(ys: Coll[A]): Boolean

/** Tests whether a predicate holds for at least one element of this collection.
* @param p the predicate used to test elements.
* @return `true` if the given predicate `p` is satisfied by at least one element of this collection, otherwise `false`
Expand Down
14 changes: 14 additions & 0 deletions core/shared/src/main/scala/sigma/data/CollsOverArrays.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil

@inline def zip[@specialized B](ys: Coll[B]): PairColl[A, B] = builder.pairColl(this, ys)

@inline def startsWith(ys: Coll[A]): Boolean = toArray.startsWith(ys.toArray)

@inline def endsWith(ys: Coll[A]): Boolean = toArray.endsWith(ys.toArray)

def append(other: Coll[A]): Coll[A] = {
if (toArray.length <= 0) return other
val result = if (VersionContext.current.isJitActivated) {
Expand Down Expand Up @@ -350,6 +354,16 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R

def zip[@specialized B](ys: Coll[B]): PairColl[(L,R), B] = builder.pairColl(this, ys)

def startsWith(ys: Coll[(L, R)]): Boolean = ys match {
case yp: PairOfCols[L, R] => ls.startsWith(yp.ls) && rs.startsWith(yp.rs)
case _ => toArray.startsWith(ys.toArray)
}

def endsWith(ys: Coll[(L, R)]): Boolean = ys match {
case yp: PairOfCols[L, R] => ls.endsWith(yp.ls) && rs.endsWith(yp.rs)
case _ => toArray.endsWith(ys.toArray)
}

override def indices: Coll[Int] = if (ls.length <= rs.length) ls.indices else rs.indices

override def flatMap[B: RType](f: ((L, R)) => Coll[B]): Coll[B] =
Expand Down
15 changes: 15 additions & 0 deletions core/shared/src/main/scala/sigma/reflection/ReflectionData.scala
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ object ReflectionData {
mkMethod(clazz, "apply", Array[Class[_]](classOf[Int])) { (obj, args) =>
obj.asInstanceOf[Coll[_]].apply(args(0).asInstanceOf[Int])
},
mkMethod(clazz, "get", Array[Class[_]](classOf[Int])) { (obj, args) =>
obj.asInstanceOf[Coll[_]].get(args(0).asInstanceOf[Int])
},
mkMethod(clazz, "append", Array[Class[_]](classOf[Coll[_]])) { (obj, args) =>
obj.asInstanceOf[Coll[Any]].append(args(0).asInstanceOf[Coll[Any]])
},
Expand All @@ -170,6 +173,18 @@ object ReflectionData {
},
mkMethod(clazz, "map", Array[Class[_]](classOf[Function1[_, _]], classOf[RType[_]])) { (obj, args) =>
obj.asInstanceOf[Coll[Any]].map(args(0).asInstanceOf[Any => Any])(args(1).asInstanceOf[RType[Any]])
},
mkMethod(clazz, "reverse", Array[Class[_]]()) { (obj, args) =>
obj.asInstanceOf[Coll[Any]].reverse
},
mkMethod(clazz, "distinct", Array[Class[_]]()) { (obj, args) =>
obj.asInstanceOf[Coll[Any]].distinct
},
mkMethod(clazz, "startsWith", Array[Class[_]](classOf[Coll[_]])) { (obj, args) =>
obj.asInstanceOf[Coll[Any]].startsWith(args(0).asInstanceOf[Coll[Any]])
},
mkMethod(clazz, "endsWith", Array[Class[_]](classOf[Coll[_]])) { (obj, args) =>
obj.asInstanceOf[Coll[Any]].endsWith(args(0).asInstanceOf[Coll[Any]])
}
)
)
Expand Down
26 changes: 26 additions & 0 deletions core/shared/src/test/scala/sigma/CollsTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,32 @@ class CollsTests extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers
}
}

property("Coll.startsWith") {
val minSuccess = minSuccessful(50)
forAll(collGen, minSuccess) { col =>
val n = col.length / 2
val prefix = col.take(n)
val pairs = col.zip(col)
pairs.startsWith(prefix.zip(prefix)) shouldBe true
col.startsWith(prefix) shouldBe true
val pairOfCols = new PairOfCols[Int, Int](col, col)
pairOfCols.startsWith(pairOfCols.take(n)) shouldBe true
}
}

property("Coll.endsWith") {
val minSuccess = minSuccessful(50)
forAll(collGen, minSuccess) { col =>
val n = col.length / 2
val suffix = col.slice(n, col.length)
col.endsWith(suffix) shouldBe true
val pairs = col.zip(col)
pairs.endsWith(suffix.zip(suffix)) shouldBe true
val pairOfCols = new PairOfCols[Int, Int](col, col)
pairOfCols.endsWith(pairOfCols.slice(n, col.length)) shouldBe true
}
}

property("Coll.equals") {
def checkColls(repl: Coll[_], coll: Coll[_]) = {
assert(coll == repl)
Expand Down
16 changes: 16 additions & 0 deletions data/shared/src/main/scala/sigma/SigmaDataReflection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,22 @@ object SigmaDataReflection {
mkMethod(clazz, "flatMap_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Function1[_,_]], classOf[ErgoTreeEvaluator])) { (obj, args) =>
obj.asInstanceOf[SCollectionMethods.type].flatMap_eval(args(0).asInstanceOf[MethodCall],
args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Any => Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator])
},
mkMethod(clazz, "reverse_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) =>
obj.asInstanceOf[SCollectionMethods.type].reverse_eval(args(0).asInstanceOf[MethodCall],
args(1).asInstanceOf[Coll[Any]])(args(2).asInstanceOf[ErgoTreeEvaluator])
},
mkMethod(clazz, "distinct_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) =>
obj.asInstanceOf[SCollectionMethods.type].distinct_eval(args(0).asInstanceOf[MethodCall],
args(1).asInstanceOf[Coll[Any]])(args(2).asInstanceOf[ErgoTreeEvaluator])
},
mkMethod(clazz, "startsWith_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) =>
obj.asInstanceOf[SCollectionMethods.type].startsWith_eval(args(0).asInstanceOf[MethodCall],
args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator])
},
mkMethod(clazz, "endsWith_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) =>
obj.asInstanceOf[SCollectionMethods.type].endsWith_eval(args(0).asInstanceOf[MethodCall],
args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator])
}
)
)
Expand Down
149 changes: 126 additions & 23 deletions data/shared/src/main/scala/sigma/ast/methods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply {
SFunc(Array(ThisType, tIV, SInt), SInt, paramIVSeq),
26, PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(10), chunkSize = 2))
.withIRInfo(MethodCallIrBuilder, javaMethodOf[Coll[_], Any, Int]("indexOf"))
.withInfo(MethodCall, "")
.withInfo(MethodCall, "Returns index of a collection element, or -1 if not found")

/** Implements evaluation of Coll.indexOf method call ErgoTree node.
* Called via reflection based on naming convention.
Expand Down Expand Up @@ -1052,8 +1052,7 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply {
baseCost = JitCost(10), perChunkCost = JitCost(1), chunkSize = 10)

val ZipMethod = SMethod(this, "zip",
SFunc(Array(ThisType, tOVColl), SCollection(STuple(tIV, tOV)), Array[STypeParam](tIV, tOV)),
29, Zip_CostKind)
SFunc(Array(ThisType, tOVColl), SCollection(STuple(tIV, tOV)), Array[STypeParam](tIV, tOV)), 29, Zip_CostKind)
.withIRInfo(MethodCallIrBuilder)
.withInfo(MethodCall, "")

Expand All @@ -1069,29 +1068,133 @@ object SCollectionMethods extends MethodsContainer with MethodByNameUnapply {
}
}

// ======== 6.0 methods below ===========

private val reverseCostKind = Append.costKind

val ReverseMethod = SMethod(this, "reverse",
SFunc(Array(ThisType), ThisType, paramIVSeq), 30, reverseCostKind)
.withIRInfo(MethodCallIrBuilder)
.withInfo(MethodCall, "")

/** Implements evaluation of Coll.reverse method call ErgoTree node.
* Called via reflection based on naming convention.
* @see SMethod.evalMethod
*/
def reverse_eval[A](mc: MethodCall, xs: Coll[A])
(implicit E: ErgoTreeEvaluator): Coll[A] = {
val m = mc.method
E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () =>
xs.reverse
}
}

private val distinctCostKind = PerItemCost(baseCost = JitCost(60), perChunkCost = JitCost(5), chunkSize = 100)

val DistinctMethod = SMethod(this, "distinct",
SFunc(Array(ThisType), ThisType, paramIVSeq), 31, distinctCostKind)
.withIRInfo(MethodCallIrBuilder)
.withInfo(MethodCall, "Returns inversed collection.")

/** Implements evaluation of Coll.reverse method call ErgoTree node.
* Called via reflection based on naming convention.
* @see SMethod.evalMethod
*/
def distinct_eval[A](mc: MethodCall, xs: Coll[A])
(implicit E: ErgoTreeEvaluator): Coll[A] = {
val m = mc.method
E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () =>
xs.distinct
}
}

private val startsWithCostKind = Zip_CostKind

val StartsWithMethod = SMethod(this, "startsWith",
SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 32, startsWithCostKind)
.withIRInfo(MethodCallIrBuilder)
.withInfo(MethodCall, "Returns true if this collection starts with given one, false otherwise.",
ArgInfo("prefix", "Collection to be checked for being a prefix of this collection."))

/** Implements evaluation of Coll.zip method call ErgoTree node.
* Called via reflection based on naming convention.
* @see SMethod.evalMethod
*/
def startsWith_eval[A](mc: MethodCall, xs: Coll[A], ys: Coll[A])
(implicit E: ErgoTreeEvaluator): Boolean = {
val m = mc.method
E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () =>
xs.startsWith(ys)
}
}

private val endsWithCostKind = Zip_CostKind

val EndsWithMethod = SMethod(this, "endsWith",
SFunc(Array(ThisType, ThisType), SBoolean, paramIVSeq), 33, endsWithCostKind)
.withIRInfo(MethodCallIrBuilder)
.withInfo(MethodCall, "Returns true if this collection ends with given one, false otherwise.",
ArgInfo("suffix", "Collection to be checked for being a suffix of this collection."))

/** Implements evaluation of Coll.zip method call ErgoTree node.
* Called via reflection based on naming convention.
* @see SMethod.evalMethod
*/
def endsWith_eval[A](mc: MethodCall, xs: Coll[A], ys: Coll[A])
(implicit E: ErgoTreeEvaluator): Boolean = {
val m = mc.method
E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () =>
xs.endsWith(ys)
}
}

val GetMethod = SMethod(this, "get",
SFunc(Array(ThisType, SInt), SOption(tIV), Array[STypeParam](tIV)), 34, ByIndex.costKind)
.withIRInfo(MethodCallIrBuilder)
.withInfo(MethodCall,
"Returns Some(element) if there is an element at given index, None otherwise.",
ArgInfo("index", "Index of an element (starting from 0).")
)

private val v5Methods = super.getMethods() ++ Seq(
SizeMethod,
GetOrElseMethod,
MapMethod,
ExistsMethod,
FoldMethod,
ForallMethod,
SliceMethod,
FilterMethod,
AppendMethod,
ApplyMethod,
IndicesMethod,
FlatMapMethod,
PatchMethod,
UpdatedMethod,
UpdateManyMethod,
IndexOfMethod,
ZipMethod
)

private val v6Methods = v5Methods ++ Seq(
ReverseMethod,
DistinctMethod,
StartsWithMethod,
EndsWithMethod,
GetMethod
)

/** This method should be overriden in derived classes to add new methods in addition to inherited.
* Typical override: `super.getMethods() ++ Seq(m1, m2, m3)`
*/
override protected def getMethods(): Seq[SMethod] = super.getMethods() ++
Seq(
SizeMethod,
GetOrElseMethod,
MapMethod,
ExistsMethod,
FoldMethod,
ForallMethod,
SliceMethod,
FilterMethod,
AppendMethod,
ApplyMethod,
IndicesMethod,
FlatMapMethod,
PatchMethod,
UpdatedMethod,
UpdateManyMethod,
IndexOfMethod,
ZipMethod
)
override protected def getMethods(): Seq[SMethod] = {
if (VersionContext.current.isV6SoftForkActivated) {
v6Methods
} else {
v5Methods
}
}

}

object STupleMethods extends MethodsContainer {
Expand Down
13 changes: 13 additions & 0 deletions sc/shared/src/main/scala/sigma/compiler/ir/GraphBuilding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,19 @@ trait GraphBuilding extends Base with DefRewriting { IR: IRContext =>
val i = asRep[Int](argsV(0))
val d = asRep[t](argsV(1))
xs.getOrElse(i, d)
case SCollectionMethods.ReverseMethod.name =>
xs.reverse
case SCollectionMethods.DistinctMethod.name =>
xs.distinct
case SCollectionMethods.StartsWithMethod.name =>
val ys = asRep[Coll[t]](argsV(0))
xs.startsWith(ys)
case SCollectionMethods.EndsWithMethod.name =>
val ys = asRep[Coll[t]](argsV(0))
xs.endsWith(ys)
case SCollectionMethods.GetMethod.name =>
val idx = asRep[Int](argsV(0))
xs.get(idx)
case _ => throwError()
}
case (opt: ROption[t]@unchecked, SOptionMethods) => method.name match {
Expand Down
16 changes: 16 additions & 0 deletions sc/shared/src/main/scala/sigma/compiler/ir/GraphIRReflection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,22 @@ object GraphIRReflection {
},
mkMethod(clazz, "exists", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.Coll[Any]].exists(args(0).asInstanceOf[ctx.Ref[Any => Boolean]])
},
// V6 methods
mkMethod(clazz, "reverse", Array[Class[_]]()) { (obj, _) =>
obj.asInstanceOf[ctx.Coll[Any]].reverse
},
mkMethod(clazz, "distinct", Array[Class[_]]()) { (obj, _) =>
obj.asInstanceOf[ctx.Coll[Any]].distinct
},
mkMethod(clazz, "startsWith", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.Coll[Any]].startsWith(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Any]]])
},
mkMethod(clazz, "endsWith", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.Coll[Any]].endsWith(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Any]]])
},
mkMethod(clazz, "get", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) =>
obj.asInstanceOf[ctx.Coll[_]].apply(args(0).asInstanceOf[ctx.Ref[Int]])
}
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import sigma.compiler.ir.{Base, IRContext}
implicit def eA: Elem[A];
def length: Ref[Int];
def apply(i: Ref[Int]): Ref[A];
def get(index: Ref[Int]): Ref[WOption[A]];
def getOrElse(index: Ref[Int], default: Ref[A]): Ref[A];
def map[B](f: Ref[scala.Function1[A, B]]): Ref[Coll[B]];
def zip[B](ys: Ref[Coll[B]]): Ref[Coll[scala.Tuple2[A, B]]];
Expand All @@ -29,6 +30,10 @@ import sigma.compiler.ir.{Base, IRContext}
def updateMany(indexes: Ref[Coll[Int]], values: Ref[Coll[A]]): Ref[Coll[A]];
def slice(from: Ref[Int], until: Ref[Int]): Ref[Coll[A]];
def append(other: Ref[Coll[A]]): Ref[Coll[A]];
def reverse: Ref[Coll[A]]
def distinct: Ref[Coll[A]]
def startsWith(ys: Ref[Coll[A]]): Ref[Boolean];
def endsWith(ys: Ref[Coll[A]]): Ref[Boolean];
};
trait CollBuilder extends Def[CollBuilder] {
def fromItems[T](items: Ref[T]*)(implicit cT: Elem[T]): Ref[Coll[T]];
Expand Down
Loading

0 comments on commit 11d869f

Please sign in to comment.