From 1f11240953658fa5422018fcff2148d774e28586 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sun, 30 Jun 2019 23:01:07 +0300 Subject: [PATCH 01/13] invalid RW rule removed, Coll.append optimized --- .../scala/scalan/primitives/LogicalOps.scala | 24 +++++++---- .../special/collection/CollsOverArrays.scala | 7 +++- library/src/main/scala/scalan/Library.scala | 1 - .../special/collections/CollsTests.scala | 42 ++++++++++++++++--- 4 files changed, 60 insertions(+), 14 deletions(-) diff --git a/core/src/main/scala/scalan/primitives/LogicalOps.scala b/core/src/main/scala/scalan/primitives/LogicalOps.scala index c56379261..278ec93ce 100644 --- a/core/src/main/scala/scalan/primitives/LogicalOps.scala +++ b/core/src/main/scala/scalan/primitives/LogicalOps.scala @@ -61,18 +61,28 @@ trait LogicalOps extends Base { self: Scalan => @inline private def matchBoolConsts(d: Def[_], lhs: Sym, rhs: Sym, ifTrue: Sym => Sym, ifFalse: Sym => Sym, ifEqual: Sym => Sym, ifNegated: Sym => Sym): Sym = lhs match { + // op(x, x) case `rhs` => ifEqual(lhs) + + // op(!x, x) case Def(ApplyUnOp(op, `rhs`)) if op == Not => ifNegated(lhs) + + // op(true, x) => ifTrue(x) | op(false, x) => ifFalse(x) case Def(Const(b: Boolean)) => if (b) ifTrue(rhs) else ifFalse(rhs) - case _ => rhs match { - case Def(Const(b: Boolean)) => - if (b) ifTrue(lhs) else ifFalse(lhs) - case Def(ApplyUnOp(op, `lhs`)) if op == Not => - ifNegated(rhs) - case _ => super.rewriteDef(d) - } + + case _ => + rhs match { + // op(x, true) => ifTrue(x) | op(false, x) => ifFalse(x) + case Def(Const(b: Boolean)) => + if (b) ifTrue(lhs) else ifFalse(lhs) + + // op(x, !x) => ifNegated(x) + case Def(ApplyUnOp(op, `lhs`)) if op == Not => + ifNegated(rhs) + case _ => super.rewriteDef(d) + } } } diff --git a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala index 27dc1c8dd..503eea11c 100644 --- a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala +++ b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala @@ -630,7 +630,12 @@ class CReplColl[@specialized A](val value: A, val length: Int)(implicit tA: RTyp } @NeverInline - def append(other: Coll[A]): Coll[A] = builder.fromArray(toArray).append(builder.fromArray(other.toArray)) + def append(other: Coll[A]): Coll[A] = other match { + case repl: ReplColl[A@unchecked] if this.value == repl.value => + new CReplColl(value, this.length + repl.length) + case _ => + builder.fromArray(toArray).append(builder.fromArray(other.toArray)) + } override def reverse: Coll[A] = this diff --git a/library/src/main/scala/scalan/Library.scala b/library/src/main/scala/scalan/Library.scala index 45043be7c..bb7a8ca7f 100644 --- a/library/src/main/scala/scalan/Library.scala +++ b/library/src/main/scala/scalan/Library.scala @@ -144,7 +144,6 @@ trait Library extends Scalan case CM.length(Def(CollConst(coll, _))) => coll.length case CM.length(CBM.fromArray(_, arr)) => arr.length case CM.length(CBM.fromItems(_, items, _)) => items.length - case IsNumericToLong(Def(IsNumericToInt(x))) if x.elem == LongElement => x // Rule: replicate(l, x).zip(replicate(l, y)) ==> replicate(l, (x,y)) case CM.zip(CBM.replicate(b1, l1, v1), CBM.replicate(b2, l2, v2)) if b1 == b2 && l1 == l2 => diff --git a/library/src/test/scala/special/collections/CollsTests.scala b/library/src/test/scala/special/collections/CollsTests.scala index d62e31b6c..236840573 100644 --- a/library/src/test/scala/special/collections/CollsTests.scala +++ b/library/src/test/scala/special/collections/CollsTests.scala @@ -1,7 +1,7 @@ package special.collections -import special.collection.{Coll, PairColl, ReplColl} -import org.scalacheck.{Gen, Shrink} +import special.collection.{ReplColl, PairOfCols, PairColl, CReplColl, Coll} +import org.scalacheck.{Shrink, Gen} import org.scalatest.{PropSpec, Matchers} import org.scalatest.prop.PropertyChecks import scalan.RType @@ -193,9 +193,37 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen } property("Coll.append") { - forAll(collGen, collGen) { (col1, col2) => - val res = col1.append(col2) - res.toArray shouldBe (col1.toArray ++ col2.toArray) + forAll(collGen, collGen, valGen, MinSuccessful(50)) { (col1, col2, v) => + + { + val res = col1.append(col2) + res.toArray shouldBe (col1.toArray ++ col2.toArray) + val pairs1 = col1.zip(col1) + val pairs2 = col2.zip(col2) + val apairs = pairs1.append(pairs2) + apairs.toArray shouldBe (pairs1.toArray ++ pairs2.toArray) + } + + { + val repl1 = builder.replicate(col1.length, v) + val repl2 = builder.replicate(col2.length, v) + val arepl = repl1.append(repl2) + assert(arepl.isInstanceOf[CReplColl[Int]]) + arepl.toArray shouldBe (repl1.toArray ++ repl2.toArray) + + val pairs1 = repl1.zip(repl1) + val pairs2 = repl2.zip(repl2) + val apairs = pairs1.append(pairs2) + apairs.toArray shouldBe (pairs1.toArray ++ pairs2.toArray) + + apairs match { + case ps: PairOfCols[_,_] => + assert(ps.ls.isInstanceOf[CReplColl[Int]]) + assert(ps.rs.isInstanceOf[CReplColl[Int]]) + case _ => + assert(false, "Invalid type") + } + } } } @@ -233,6 +261,10 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen res.toArray shouldBe col.toArray.reverse val pairs = col.zip(col) pairs.reverse.toArray shouldBe pairs.toArray.reverse +// TODO should work +// val c1 = col.asInstanceOf[Coll[Any]] +// val appended = c1.append(c1) +// appended.toArray shouldBe (c1.toArray ++ c1.toArray) } } From 8bfc47184f832061cfa58062ba697dbba1f0bbfe Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 1 Jul 2019 00:43:32 +0300 Subject: [PATCH 02/13] clarify matchBoolConsts --- core/src/main/scala/scalan/primitives/LogicalOps.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/scalan/primitives/LogicalOps.scala b/core/src/main/scala/scalan/primitives/LogicalOps.scala index 278ec93ce..0da83fafb 100644 --- a/core/src/main/scala/scalan/primitives/LogicalOps.scala +++ b/core/src/main/scala/scalan/primitives/LogicalOps.scala @@ -65,7 +65,7 @@ trait LogicalOps extends Base { self: Scalan => case `rhs` => ifEqual(lhs) - // op(!x, x) + // op(!x, x) => ifNegated(!x) case Def(ApplyUnOp(op, `rhs`)) if op == Not => ifNegated(lhs) @@ -79,7 +79,7 @@ trait LogicalOps extends Base { self: Scalan => case Def(Const(b: Boolean)) => if (b) ifTrue(lhs) else ifFalse(lhs) - // op(x, !x) => ifNegated(x) + // op(x, !x) => ifNegated(!x) case Def(ApplyUnOp(op, `lhs`)) if op == Not => ifNegated(rhs) case _ => super.rewriteDef(d) From 80741c93705b2e12a464bdcd40c88465ebe5b9cc Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 1 Jul 2019 08:32:25 +0300 Subject: [PATCH 03/13] turn-off map map rule --- library/src/main/scala/scalan/Library.scala | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/library/src/main/scala/scalan/Library.scala b/library/src/main/scala/scalan/Library.scala index bb7a8ca7f..54be595eb 100644 --- a/library/src/main/scala/scalan/Library.scala +++ b/library/src/main/scala/scalan/Library.scala @@ -158,16 +158,16 @@ trait Library extends Scalan case WA.zip(WA.map(xs, IsProjectFirst(_)), WA.map(ys, IsProjectSecond(_))) if xs == ys => xs case CM.zip(CM.map(xs, IsProjectFirst(_)), CM.map(ys, IsProjectSecond(_))) if xs == ys => xs - case WA.map(WA.map(_xs, f: RFunc[a, b]), _g: RFunc[_,c]) => - implicit val ea = f.elem.eDom - val xs = _xs.asRep[WArray[a]] - val g = _g.asRep[b => c] - xs.map(fun { x: Rep[a] => g(f(x)) }) - case CM.map(CM.map(_xs, f: RFunc[a, b]), _g: RFunc[_,c]) => - implicit val ea = f.elem.eDom - val xs = _xs.asRep[Coll[a]] - val g = _g.asRep[b => c] - xs.map(fun { x: Rep[a] => g(f(x)) }) +// case WA.map(WA.map(_xs, f: RFunc[a, b]), _g: RFunc[_,c]) => +// implicit val ea = f.elem.eDom +// val xs = _xs.asRep[WArray[a]] +// val g = _g.asRep[b => c] +// xs.map(fun { x: Rep[a] => g(f(x)) }) +// case CM.map(CM.map(_xs, f: RFunc[a, b]), _g: RFunc[_,c]) => +// implicit val ea = f.elem.eDom +// val xs = _xs.asRep[Coll[a]] +// val g = _g.asRep[b => c] +// xs.map(fun { x: Rep[a] => g(f(x)) }) case CM.map(xs, Def(IdentityLambda())) => xs // case CM.map(xs, Def(ConstantLambda(res))) => From 9d5f48d6837d78dca499e220b82892bb018785a9 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 1 Jul 2019 09:10:42 +0300 Subject: [PATCH 04/13] remove unnecessary code --- core/src/main/scala/scalan/Scalan.scala | 12 +++---- core/src/main/scala/scalan/TypeDescs.scala | 8 ++--- core/src/main/scala/scalan/Views.scala | 34 +++++++++---------- .../scalan/compilation/GraphVizExport.scala | 2 +- .../scalan/primitives/UniversalOps.scala | 8 ++--- .../scala/scalan/staged/SlicingTests.scala | 5 +-- .../compilation/kotlin/KotlinCompiler.scala | 2 +- .../kotlin/KotlinFileCodegen.scala | 8 +++-- library/src/main/scala/scalan/Library.scala | 4 +-- .../special/collection/impl/CollsImpl.scala | 31 +++++++++-------- .../wrappers/scala/impl/WArraysImpl.scala | 30 ++++++++-------- .../wrappers/scala/impl/WOptionsImpl.scala | 31 +++++++++-------- .../scala/scalan/json/ToolkitScalan.scala | 5 +-- 13 files changed, 93 insertions(+), 87 deletions(-) diff --git a/core/src/main/scala/scalan/Scalan.scala b/core/src/main/scala/scalan/Scalan.scala index 38a12444e..773939a4f 100644 --- a/core/src/main/scala/scalan/Scalan.scala +++ b/core/src/main/scala/scalan/Scalan.scala @@ -6,12 +6,12 @@ import scalan.staged.Transforming class Scalan extends Base - with Debugging +// with Debugging with TypeDescs with Metadata with Proxy with Tuples - with Loops +// with Loops with TypeSum with NumericOps with UnBinOps @@ -22,11 +22,11 @@ class Scalan with UniversalOps with Functions with IfThenElse - with PatternMatching +// with PatternMatching with Transforming - with Analyzing - with Exceptions - with StringOps +// with Analyzing +// with Exceptions +// with StringOps with RewriteRules with GraphVizExport with ViewsModule diff --git a/core/src/main/scala/scalan/TypeDescs.scala b/core/src/main/scala/scalan/TypeDescs.scala index dd620988b..0ee49d1cd 100644 --- a/core/src/main/scala/scalan/TypeDescs.scala +++ b/core/src/main/scala/scalan/TypeDescs.scala @@ -399,9 +399,9 @@ trait TypeDescs extends Base { self: Scalan => def <:<(e: Elem[_]) = tag.tpe <:< e.tag.tpe def >:>(e: Elem[_]) = e <:< this - if (Base.isDebug) { - debug$ElementCounter(this) += 1 - } +// if (Base.isDebug) { +// debug$ElementCounter(this) += 1 +// } } object Elem { implicit def rtypeToElem[SA, A](tSA: RType[SA])(implicit lA: Liftables.Liftable[SA,A]): Elem[A] = lA.eW @@ -455,7 +455,7 @@ trait TypeDescs extends Base { self: Scalan => def invokeUnlifted(e: Elem[_], mc: MethodCall, dataEnv: DataEnv): AnyRef = e.invokeUnlifted(mc, dataEnv) - private lazy val debug$ElementCounter = counter[Elem[_]] +// private lazy val debug$ElementCounter = counter[Elem[_]] private[scalan] def getConstructor(clazz: Class[_]) = { val constructors = clazz.getDeclaredConstructors() diff --git a/core/src/main/scala/scalan/Views.scala b/core/src/main/scala/scalan/Views.scala index e7c4c7998..0c0d2f266 100644 --- a/core/src/main/scala/scalan/Views.scala +++ b/core/src/main/scala/scalan/Views.scala @@ -628,23 +628,23 @@ trait ViewsModule extends impl.ViewsDefs { self: Scalan => // ViewArray(parRes, iso) // Rule: loop(V(start, iso), step, isMatch) ==> iso.to(loop(start, iso.to >> step >> iso.from, iso.to >> isMatch)) - case LoopUntil(HasViews(startWithoutViews, iso: Iso[a, b]), step, isMatch) => - val start1 = startWithoutViews.asRep[a] - implicit val eA = iso.eFrom - implicit val eB = iso.eTo - val step1 = fun { (x: Rep[a]) => - val x_viewed = iso.to(x) - val res_viewed = step.asRep[b => b](x_viewed) // mirrorApply(step.asRep[b => b], x_viewed) - val res = iso.from(res_viewed) - res - } - val isMatch1 = fun { (x: Rep[a]) => - val x_viewed = iso.to(x) - val res = isMatch.asRep[b => Boolean](x_viewed) // mirrorApply(isMatch.asRep[b => Boolean], x_viewed) - res - } - val loopRes = LoopUntil(start1, step1, isMatch1) - iso.to(loopRes) +// case LoopUntil(HasViews(startWithoutViews, iso: Iso[a, b]), step, isMatch) => +// val start1 = startWithoutViews.asRep[a] +// implicit val eA = iso.eFrom +// implicit val eB = iso.eTo +// val step1 = fun { (x: Rep[a]) => +// val x_viewed = iso.to(x) +// val res_viewed = step.asRep[b => b](x_viewed) // mirrorApply(step.asRep[b => b], x_viewed) +// val res = iso.from(res_viewed) +// res +// } +// val isMatch1 = fun { (x: Rep[a]) => +// val x_viewed = iso.to(x) +// val res = isMatch.asRep[b => Boolean](x_viewed) // mirrorApply(isMatch.asRep[b => Boolean], x_viewed) +// res +// } +// val loopRes = LoopUntil(start1, step1, isMatch1) +// iso.to(loopRes) case _ => super.rewriteViews(d) } diff --git a/core/src/main/scala/scalan/compilation/GraphVizExport.scala b/core/src/main/scala/scalan/compilation/GraphVizExport.scala index 5afda47b3..afa653ab2 100644 --- a/core/src/main/scala/scalan/compilation/GraphVizExport.scala +++ b/core/src/main/scala/scalan/compilation/GraphVizExport.scala @@ -120,7 +120,7 @@ trait GraphVizExport { self: Scalan => case First(pair) => s"$pair._1" case Second(pair) => s"$pair._2" case IfThenElse(c, t, e) => s"if ($c) $t else $e" - case LoopUntil(start, step, isMatch) => s"from $start do $step until $isMatch" +// case LoopUntil(start, step, isMatch) => s"from $start do $step until $isMatch" case ApplyBinOp(op, lhs, rhs) => s"$lhs ${op.opName} $rhs" case ApplyUnOp(op, arg) => op match { case NumericToFloat(_) => s"$arg.toFloat" diff --git a/core/src/main/scala/scalan/primitives/UniversalOps.scala b/core/src/main/scala/scalan/primitives/UniversalOps.scala index f1f79d0d2..59412e090 100644 --- a/core/src/main/scala/scalan/primitives/UniversalOps.scala +++ b/core/src/main/scala/scalan/primitives/UniversalOps.scala @@ -72,8 +72,8 @@ trait UniversalOps extends Base { scalan: Scalan => def hashCodeRep: Rep[Int] = HashCode[A]().apply(x) def toStringRep = ToString[A]().apply(x) } - override def rewriteDef[T](d: Def[T]) = d match { - case ApplyUnOp(ToString(), x) if x.elem == StringElement => x - case _ => super.rewriteDef(d) - } +// override def rewriteDef[T](d: Def[T]) = d match { +// case ApplyUnOp(ToString(), x) if x.elem == StringElement => x +// case _ => super.rewriteDef(d) +// } } diff --git a/core/src/test/scala/scalan/staged/SlicingTests.scala b/core/src/test/scala/scalan/staged/SlicingTests.scala index 8f28af6a9..6f47090dc 100644 --- a/core/src/test/scala/scalan/staged/SlicingTests.scala +++ b/core/src/test/scala/scalan/staged/SlicingTests.scala @@ -1,11 +1,12 @@ package scalan.staged import scalan.compilation.{SlicingCompiler, DummyCompiler} -import scalan.{BaseTests, TestContexts, Scalan, Lazy} +import scalan.primitives.StringOps +import scalan.{BaseTests, Lazy, TestContexts, Scalan} abstract class AbstractSlicingTests extends BaseTests with TestContexts { - class Ctx extends TestContext with Slicing { + class Ctx extends TestContext with Slicing with StringOps { def createSliceAnalyzer = new SliceAnalyzer val eInt = element[Int] diff --git a/kotlin-backend/src/main/scala/scalan/compilation/kotlin/KotlinCompiler.scala b/kotlin-backend/src/main/scala/scalan/compilation/kotlin/KotlinCompiler.scala index 185566e32..063c5c4ed 100644 --- a/kotlin-backend/src/main/scala/scalan/compilation/kotlin/KotlinCompiler.scala +++ b/kotlin-backend/src/main/scala/scalan/compilation/kotlin/KotlinCompiler.scala @@ -6,7 +6,7 @@ import scalan.Scalan import scalan.compilation.{CodegenConfig, ScalanCompiler, GraphVizConfig} import scalan.primitives.Blocks -class KotlinCompiler[+IR <: Scalan with Blocks](val _scalan: IR, val config: CodegenConfig) +class KotlinCompiler[+IR <: ScalanEx](val _scalan: IR, val config: CodegenConfig) extends ScalanCompiler[IR, KotlinFileCodegen[IR]](_scalan) { import scalan._ diff --git a/kotlin-backend/src/main/scala/scalan/compilation/kotlin/KotlinFileCodegen.scala b/kotlin-backend/src/main/scala/scalan/compilation/kotlin/KotlinFileCodegen.scala index 5ccd8fe62..e01e7b770 100644 --- a/kotlin-backend/src/main/scala/scalan/compilation/kotlin/KotlinFileCodegen.scala +++ b/kotlin-backend/src/main/scala/scalan/compilation/kotlin/KotlinFileCodegen.scala @@ -2,16 +2,18 @@ package scalan.compilation.kotlin import java.io.PrintWriter -import scalan.{Scalan, TypeDesc} +import scalan.{TypeDesc, Scalan} import scalan.compilation.{IndentLevel, FileCodegen, CodegenConfig} import scalan.meta.ScalanAst._ import scalan.util.PrintExtensions._ import scalan.meta.{SSymName, ScalanAstTransformers} -import scalan.primitives.Blocks +import scalan.primitives.{Blocks, StringOps} case class GenCtx(module: SUnitDef, writer: PrintWriter) -class KotlinFileCodegen[+IR <: Scalan with Blocks](_scalan: IR, config: CodegenConfig) extends FileCodegen(_scalan, config) { +class ScalanEx extends Scalan with Blocks with StringOps + +class KotlinFileCodegen[+IR <: ScalanEx](_scalan: IR, config: CodegenConfig) extends FileCodegen(_scalan, config) { import scalan._ implicit val context = astContext val PairType = SSymName("kotlin", "Pair") diff --git a/library/src/main/scala/scalan/Library.scala b/library/src/main/scala/scalan/Library.scala index 54be595eb..029e50375 100644 --- a/library/src/main/scala/scalan/Library.scala +++ b/library/src/main/scala/scalan/Library.scala @@ -155,8 +155,8 @@ trait Library extends Scalan b.replicate(l, Apply(f, v, false)) // Rule: xs.map(_._1).zip(xs.map(_._2)) ==> xs - case WA.zip(WA.map(xs, IsProjectFirst(_)), WA.map(ys, IsProjectSecond(_))) if xs == ys => xs - case CM.zip(CM.map(xs, IsProjectFirst(_)), CM.map(ys, IsProjectSecond(_))) if xs == ys => xs +// case WA.zip(WA.map(xs, IsProjectFirst(_)), WA.map(ys, IsProjectSecond(_))) if xs == ys => xs +// case CM.zip(CM.map(xs, IsProjectFirst(_)), CM.map(ys, IsProjectSecond(_))) if xs == ys => xs // case WA.map(WA.map(_xs, f: RFunc[a, b]), _g: RFunc[_,c]) => // implicit val ea = f.elem.eDom diff --git a/library/src/main/scala/special/collection/impl/CollsImpl.scala b/library/src/main/scala/special/collection/impl/CollsImpl.scala index a52d0b08f..894765fc4 100644 --- a/library/src/main/scala/special/collection/impl/CollsImpl.scala +++ b/library/src/main/scala/special/collection/impl/CollsImpl.scala @@ -1202,26 +1202,27 @@ implicit val eV = proj.elem.eRange type RepColl[A] = Rep[Coll[A]] + // manual fix override def rewriteDef[T](d: Def[T]) = d match { - case view1@ViewColl(Def(view2@ViewColl(arr, innerIso2)), innerIso1) => - val compIso = composeIso(innerIso1, innerIso2) - implicit val eAB = compIso.eTo - ViewColl(arr, compIso) +// case view1@ViewColl(Def(view2@ViewColl(arr, innerIso2)), innerIso1) => +// val compIso = composeIso(innerIso1, innerIso2) +// implicit val eAB = compIso.eTo +// ViewColl(arr, compIso) case CollMethods.map(xs, f) => (xs, f) match { case (_, Def(IdentityLambda())) => xs - case (xs: RepColl[a] @unchecked, LambdaResultHasViews(f, iso: Iso[b, c])) => - val f1 = asRep[a => c](f) - implicit val eB = iso.eFrom - val s = xs.map(f1 >> iso.fromFun) - val res = ViewColl(s, iso) - res - case (HasViews(source, Def(contIso: CollIso[a, b])), f: RFunc[_, c]@unchecked) => - val f1 = asRep[b => c](f) - val iso = contIso.innerIso - implicit val eC = f1.elem.eRange - asRep[Coll[a]](source).map(iso.toFun >> f1) +// case (xs: RepColl[a] @unchecked, LambdaResultHasViews(f, iso: Iso[b, c])) => +// val f1 = asRep[a => c](f) +// implicit val eB = iso.eFrom +// val s = xs.map(f1 >> iso.fromFun) +// val res = ViewColl(s, iso) +// res +// case (HasViews(source, Def(contIso: CollIso[a, b])), f: RFunc[_, c]@unchecked) => +// val f1 = asRep[b => c](f) +// val iso = contIso.innerIso +// implicit val eC = f1.elem.eRange +// asRep[Coll[a]](source).map(iso.toFun >> f1) case _ => super.rewriteDef(d) } diff --git a/library/src/main/scala/wrappers/scala/impl/WArraysImpl.scala b/library/src/main/scala/wrappers/scala/impl/WArraysImpl.scala index 946b9ee4b..dc412c3bf 100644 --- a/library/src/main/scala/wrappers/scala/impl/WArraysImpl.scala +++ b/library/src/main/scala/wrappers/scala/impl/WArraysImpl.scala @@ -491,25 +491,25 @@ object WArray extends EntityObject("WArray") { type RepWArray[T] = Rep[WArray[T]] override def rewriteDef[T](d: Def[T]) = d match { - case view1@ViewWArray(Def(view2@ViewWArray(arr, innerIso2)), innerIso1) => - val compIso = composeIso(innerIso1, innerIso2) - implicit val eAB = compIso.eTo - ViewWArray(arr, compIso) +// case view1@ViewWArray(Def(view2@ViewWArray(arr, innerIso2)), innerIso1) => +// val compIso = composeIso(innerIso1, innerIso2) +// implicit val eAB = compIso.eTo +// ViewWArray(arr, compIso) case WArrayMethods.map(xs, f) => (xs, f) match { case (_, Def(IdentityLambda())) => xs - case (xs: RepWArray[a] @unchecked, LambdaResultHasViews(f, iso: Iso[b, c])) => - val f1 = asRep[a => c](f) - implicit val eB = iso.eFrom - val s = xs.map(f1 >> iso.fromFun) - val res = ViewWArray(s, iso) - res - case (HasViews(source, Def(contIso: WArrayIso[a, b])), f: RFunc[_, c]@unchecked) => - val f1 = asRep[b => c](f) - val iso = contIso.innerIso - implicit val eC = f1.elem.eRange - asRep[WArray[a]](source).map(iso.toFun >> f1) +// case (xs: RepWArray[a] @unchecked, LambdaResultHasViews(f, iso: Iso[b, c])) => +// val f1 = asRep[a => c](f) +// implicit val eB = iso.eFrom +// val s = xs.map(f1 >> iso.fromFun) +// val res = ViewWArray(s, iso) +// res +// case (HasViews(source, Def(contIso: WArrayIso[a, b])), f: RFunc[_, c]@unchecked) => +// val f1 = asRep[b => c](f) +// val iso = contIso.innerIso +// implicit val eC = f1.elem.eRange +// asRep[WArray[a]](source).map(iso.toFun >> f1) case _ => super.rewriteDef(d) } diff --git a/library/src/main/scala/wrappers/scala/impl/WOptionsImpl.scala b/library/src/main/scala/wrappers/scala/impl/WOptionsImpl.scala index efa82ec3f..d0a5ca90d 100644 --- a/library/src/main/scala/wrappers/scala/impl/WOptionsImpl.scala +++ b/library/src/main/scala/wrappers/scala/impl/WOptionsImpl.scala @@ -418,26 +418,27 @@ object WOption extends EntityObject("WOption") { type RepWOption[A] = Rep[WOption[A]] + // manual fix override def rewriteDef[T](d: Def[T]) = d match { - case view1@ViewWOption(Def(view2@ViewWOption(arr, innerIso2)), innerIso1) => - val compIso = composeIso(innerIso1, innerIso2) - implicit val eAB = compIso.eTo - ViewWOption(arr, compIso) +// case view1@ViewWOption(Def(view2@ViewWOption(arr, innerIso2)), innerIso1) => +// val compIso = composeIso(innerIso1, innerIso2) +// implicit val eAB = compIso.eTo +// ViewWOption(arr, compIso) case WOptionMethods.map(xs, f) => (xs, f) match { case (_, Def(IdentityLambda())) => xs - case (xs: RepWOption[a] @unchecked, LambdaResultHasViews(f, iso: Iso[b, c])) => - val f1 = asRep[a => c](f) - implicit val eB = iso.eFrom - val s = xs.map(f1 >> iso.fromFun) - val res = ViewWOption(s, iso) - res - case (HasViews(source, Def(contIso: WOptionIso[a, b])), f: RFunc[_, c]@unchecked) => - val f1 = asRep[b => c](f) - val iso = contIso.innerIso - implicit val eC = f1.elem.eRange - asRep[WOption[a]](source).map(iso.toFun >> f1) +// case (xs: RepWOption[a] @unchecked, LambdaResultHasViews(f, iso: Iso[b, c])) => +// val f1 = asRep[a => c](f) +// implicit val eB = iso.eFrom +// val s = xs.map(f1 >> iso.fromFun) +// val res = ViewWOption(s, iso) +// res +// case (HasViews(source, Def(contIso: WOptionIso[a, b])), f: RFunc[_, c]@unchecked) => +// val f1 = asRep[b => c](f) +// val iso = contIso.innerIso +// implicit val eC = f1.elem.eRange +// asRep[WOption[a]](source).map(iso.toFun >> f1) case _ => super.rewriteDef(d) } diff --git a/toolkit/src/main/scala/scalan/json/ToolkitScalan.scala b/toolkit/src/main/scala/scalan/json/ToolkitScalan.scala index 5eecb70c0..1020e1723 100644 --- a/toolkit/src/main/scala/scalan/json/ToolkitScalan.scala +++ b/toolkit/src/main/scala/scalan/json/ToolkitScalan.scala @@ -2,9 +2,10 @@ package scalan.json import scalan.meta.{Parsers, AstContextBase} import scalan.meta.ScalanAst.SUnitDef +import scalan.primitives.StringOps import scala.collection.mutable -import scalan.{ModuleInfo, Scalan, Modules} +import scalan.{ModuleInfo, Modules, Scalan} trait ParsedModules extends Modules { scalan: Scalan => lazy val parsers = { @@ -33,4 +34,4 @@ trait ParsedModules extends Modules { scalan: Scalan => } } -class ToolkitScalan extends Scalan with ParsedModules +class ToolkitScalan extends Scalan with ParsedModules with StringOps From 933ce449d1fb8eeb727585048d9d0557b7f1862b Mon Sep 17 00:00:00 2001 From: Eugene Gostkin Date: Mon, 15 Jul 2019 20:22:50 +0300 Subject: [PATCH 05/13] Use cfor in range-based loops --- .../special/collection/CollsOverArrays.scala | 42 ++++++------------- .../scala/special/collection/Helpers.scala | 4 +- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala index 503eea11c..e76a4a86e 100644 --- a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala +++ b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala @@ -121,12 +121,11 @@ class CollOverArray[@specialized A](val toArray: Array[A])(implicit tA: RType[A] override def updateMany(indexes: Coll[Int], values: Coll[A]): Coll[A] = { requireSameLength(indexes, values) val resArr = toArray.clone() - var i = 0 - while (i < indexes.length) { + val limit = indexes.length + cfor(0)(_ < limit, _ + 1) { i => val pos = indexes(i) if (pos < 0 || pos >= toArray.length) throw new IndexOutOfBoundsException(pos.toString) resArr(pos) = values(i) - i += 1 } builder.fromArray(resArr) } @@ -380,11 +379,9 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R @NeverInline override def exists(p: ((L, R)) => Boolean): Boolean = { val len = ls.length - var i = 0 - while (i < len) { + cfor(0)(_ < len, _ + 1) { i => val found = p((ls(i), rs(i))) if (found) return true - i += 1 } false } @@ -392,11 +389,9 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R @NeverInline override def forall(p: ((L, R)) => Boolean): Boolean = { val len = ls.length - var i = 0 - while (i < len) { + cfor(0)(_ < len, _ + 1) { i => val ok = p((ls(i), rs(i))) if (!ok) return false - i += 1 } true } @@ -405,8 +400,7 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R val len = ls.length val resL: Buffer[L] = Buffer.empty[L](ls.tItem.classTag) val resR: Buffer[R] = Buffer.empty[R](rs.tItem.classTag) - var i = 0 - while (i < len) { + cfor(0)(_ < len, _ + 1) { i => val l = ls.apply(i) val r = rs.apply(i) val ok = p((l, r)) @@ -414,7 +408,6 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R resL += l resR += r } - i += 1 } builder.pairCollFromArrays(resL.toArray(), resR.toArray()) } @@ -510,13 +503,12 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R requireSameLength(indexes, values) val resL = ls.toArray.clone() val resR = rs.toArray.clone() - var i = 0 - while (i < indexes.length) { + val limit = indexes.length + cfor(0)(_ < limit, _ + 1) { i => val pos = indexes(i) if (pos < 0 || pos >= length) throw new IndexOutOfBoundsException(pos.toString) resL(pos) = values(i)._1 resR(pos) = values(i)._2 - i += 1 } builder.pairColl(builder.fromArray(resL), builder.fromArray(resR)) } @@ -541,17 +533,13 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R resR += item._2 } } - var i = 0 - val thisLen = math.min(ls.length, rs.length) - while (i < thisLen) { + val thisLimit = math.min(ls.length, rs.length) + cfor(0)(_ < thisLimit, _ + 1) { i => addToSet((ls(i), rs(i))) - i += 1 } - i = 0 - val thatLen = that.length - while (i < thatLen) { + val thatLimit = that.length + cfor(0)(_ < thatLimit, _ + 1) { i => addToSet(that(i)) - i += 1 } builder.pairCollFromArrays(resL.toArray, resR.toArray) } @@ -707,12 +695,10 @@ class CReplColl[@specialized A](val value: A, val length: Int)(implicit tA: RTyp override def updateMany(indexes: Coll[Int], values: Coll[A]): Coll[A] = { requireSameLength(indexes, values) val resArr = toArray.clone() - var i = 0 - while (i < indexes.length) { + cfor(0)(_ < indexes.length, _ + 1) { i => val pos = indexes(i) if (pos < 0 || pos >= length) throw new IndexOutOfBoundsException(pos.toString) resArr(pos) = values(i) - i += 1 } builder.fromArray(resArr) } @@ -722,10 +708,8 @@ class CReplColl[@specialized A](val value: A, val length: Int)(implicit tA: RTyp if (length <= 0) return builder.pairColl(builder.emptyColl[K], builder.emptyColl[V]) val (k, v) = m(value) var reducedV = v - var i = 1 - while (i < length) { + cfor(1)(_ < length, _ + 1) { i => reducedV = r((reducedV, v)) - i += 1 } builder.pairColl(builder.fromItems(k), builder.fromItems(reducedV)) } diff --git a/library-impl/src/main/scala/special/collection/Helpers.scala b/library-impl/src/main/scala/special/collection/Helpers.scala index 9f131b3f5..1531eb47e 100644 --- a/library-impl/src/main/scala/special/collection/Helpers.scala +++ b/library-impl/src/main/scala/special/collection/Helpers.scala @@ -23,9 +23,8 @@ object Helpers { val keyPositions = new java.util.HashMap[K, Int](32) val keys = mutable.ArrayBuilder.make[K] val values = Array.ofDim[V](arr.length) - var i = 0 var nValues = 0 - while (i < arr.length) { + cfor(0)(_ < arr.length, _ + 1) { i => val (key, value) = m(arr(i)) val pos = keyPositions.getOrDefault(key, 0) if (pos == 0) { @@ -36,7 +35,6 @@ object Helpers { } else { values(pos - 1) = r((values(pos - 1), value)) } - i += 1 } val resValues = Array.ofDim[V](nValues) Array.copy(values, 0, resValues, 0, nValues) From 17eb7b13d2c791ef6117278ff90e78927154a740 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Thu, 18 Jul 2019 17:51:32 +0300 Subject: [PATCH 06/13] [fix] CReplColl.append potential bug --- .../src/main/scala/special/collection/CollsOverArrays.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala index e76a4a86e..1ebfd641d 100644 --- a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala +++ b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala @@ -619,7 +619,7 @@ class CReplColl[@specialized A](val value: A, val length: Int)(implicit tA: RTyp @NeverInline def append(other: Coll[A]): Coll[A] = other match { - case repl: ReplColl[A@unchecked] if this.value == repl.value => + case repl: ReplColl[A@unchecked] if this.value == repl.value && this.length > 0 && repl.length > 0 => new CReplColl(value, this.length + repl.length) case _ => builder.fromArray(toArray).append(builder.fromArray(other.toArray)) From 8f5704a4a5864a71b69b556d56faef883346b38d Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Thu, 18 Jul 2019 17:52:04 +0300 Subject: [PATCH 07/13] hard-fork-changes.md added --- docs/hard-fork-changes.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 docs/hard-fork-changes.md diff --git a/docs/hard-fork-changes.md b/docs/hard-fork-changes.md new file mode 100644 index 000000000..1df6df2a9 --- /dev/null +++ b/docs/hard-fork-changes.md @@ -0,0 +1,24 @@ +## A list of hard-fork changes + +Please describe here all changes which may lead to hard fork (HF for short). + +**Pull requests based on the next HF branch should be rejected, +if they contain HF change, which is not described here**. + +### Hard-fork changes in v0.6.0 (since v0.5.0) + +1. Removed RW rule `case IsNumericToLong(Def(IsNumericToInt(x))) if x.elem == LongElement => x` + This is a bug, but its fix may lead to hard-fork. + Example: + The follwing expression `MaxLong.toInt.toLong == MaxLong` + with this rule will evaluate to `true`, + without this rule will throw ArithmeticException. + MaxLong here can be any Long which is larger than MaxInt. + With this rule the expression becomes `MaxLong == MaxLong` + + 2. Removed RW rule `case CM.map(CM.map(_xs, f: RFunc[a, b]), _g: RFunc[_,c]) =>`. + Such kind of transformations in general don't preserve expression eqvivalence + in a strict (Call-By-Value) language. + Having such rule is another bug, which is safe by itself, but cannot + be fixed without HF. + From c6902316b5ce9b645b6f83029e93cb0bf222204c Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Thu, 18 Jul 2019 18:17:56 +0300 Subject: [PATCH 08/13] fixed property("Coll.append") --- library/src/test/scala/special/collections/CollsTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/test/scala/special/collections/CollsTests.scala b/library/src/test/scala/special/collections/CollsTests.scala index 236840573..d641a2f84 100644 --- a/library/src/test/scala/special/collections/CollsTests.scala +++ b/library/src/test/scala/special/collections/CollsTests.scala @@ -208,7 +208,7 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen val repl1 = builder.replicate(col1.length, v) val repl2 = builder.replicate(col2.length, v) val arepl = repl1.append(repl2) - assert(arepl.isInstanceOf[CReplColl[Int]]) + assert(!(repl1.isEmpty || repl2.isEmpty) && arepl.isInstanceOf[CReplColl[Int]]) arepl.toArray shouldBe (repl1.toArray ++ repl2.toArray) val pairs1 = repl1.zip(repl1) From 00e966f49a5ad97421a5b460a54ee6f7c81f54ec Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Thu, 18 Jul 2019 20:08:13 +0300 Subject: [PATCH 09/13] fixed property("Coll.append") (part 2) --- .../special/collections/CollsTests.scala | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/library/src/test/scala/special/collections/CollsTests.scala b/library/src/test/scala/special/collections/CollsTests.scala index d641a2f84..cd9b6f034 100644 --- a/library/src/test/scala/special/collections/CollsTests.scala +++ b/library/src/test/scala/special/collections/CollsTests.scala @@ -193,7 +193,7 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen } property("Coll.append") { - forAll(collGen, collGen, valGen, MinSuccessful(50)) { (col1, col2, v) => + forAll(collGen, collGen, valGen, MinSuccessful(100)) { (col1, col2, v) => { val res = col1.append(col2) @@ -208,7 +208,7 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen val repl1 = builder.replicate(col1.length, v) val repl2 = builder.replicate(col2.length, v) val arepl = repl1.append(repl2) - assert(!(repl1.isEmpty || repl2.isEmpty) && arepl.isInstanceOf[CReplColl[Int]]) + arepl.toArray shouldBe (repl1.toArray ++ repl2.toArray) val pairs1 = repl1.zip(repl1) @@ -216,13 +216,17 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen val apairs = pairs1.append(pairs2) apairs.toArray shouldBe (pairs1.toArray ++ pairs2.toArray) - apairs match { - case ps: PairOfCols[_,_] => - assert(ps.ls.isInstanceOf[CReplColl[Int]]) - assert(ps.rs.isInstanceOf[CReplColl[Int]]) - case _ => - assert(false, "Invalid type") + if (repl1.nonEmpty && repl2.nonEmpty) { + assert(arepl.isInstanceOf[CReplColl[Int]]) + apairs match { + case ps: PairOfCols[_,_] => + assert(ps.ls.isInstanceOf[CReplColl[Int]]) + assert(ps.rs.isInstanceOf[CReplColl[Int]]) + case _ => + assert(false, "Invalid type") + } } + } } } From 069b3ffa9ed8a0cc2c6d2b0f529cf9b19dad8d75 Mon Sep 17 00:00:00 2001 From: Eugene Gostkin Date: Mon, 5 Aug 2019 11:05:50 +0300 Subject: [PATCH 10/13] Implement RType generators (#44) * Implement generators * Simplify implementation * Some fixes * Enhance generators * Add coverage test for FullTypeGenerator * Update test name, move RTypeGenCoverageChecker to new file * Refactoring * Cosmetics * Review comments fix, add OptionType and CollType support --- .../scalan/RTypeGenCoverageChecker.scala | 61 +++++++++ library/src/test/scala/scalan/RTypeGens.scala | 120 ++++++++++++++++++ .../src/test/scala/scalan/RTypeTests.scala | 26 ++++ 3 files changed, 207 insertions(+) create mode 100644 library/src/test/scala/scalan/RTypeGenCoverageChecker.scala create mode 100644 library/src/test/scala/scalan/RTypeGens.scala create mode 100644 library/src/test/scala/scalan/RTypeTests.scala diff --git a/library/src/test/scala/scalan/RTypeGenCoverageChecker.scala b/library/src/test/scala/scalan/RTypeGenCoverageChecker.scala new file mode 100644 index 000000000..3634f43f4 --- /dev/null +++ b/library/src/test/scala/scalan/RTypeGenCoverageChecker.scala @@ -0,0 +1,61 @@ +package scalan + +import spire.syntax.all.cfor + +trait RTypeGenCoverageChecker { + def consider(item: RType[_]): Unit + def isFullyCovered(depth: Int): Boolean +} + +class FullTypeCoverageChecker extends RTypeGenCoverageChecker { + import RType._ + import special.collection._ + + private type TypePosition = (String, Int) + private var typePositions: Set[TypePosition] = Set.empty + + override def consider(item: RType[_]): Unit = { + decomposeValue(item) + } + + override def isFullyCovered(depth: Int): Boolean = { + val names = Seq("PrimitiveType", "PairType", "ArrayType", "StringType", "CollType", "ReplCollType", "OptionType") + cfor(0)(_ < depth, _ + 1) { i => + for (name <- names) { + + if (!typePositions.contains(new TypePosition(name, i))) { + println(s"Type ${name} is not found at depth ${i}") + return false + } + } + } + true + } + + private def attachResult(typeName: String, depth: Int) = { + val newTypePosition: TypePosition = (typeName, depth) + typePositions = typePositions + newTypePosition + } + + private def decomposeValue(item: RType[_], depth: Int = 0): Unit = item match { + case prim: PrimitiveType[a] => attachResult("PrimitiveType", depth) + case pair: PairType[a, b]@unchecked => + attachResult("PairType", depth) + decomposeValue(pair.tFst, depth + 1) + decomposeValue(pair.tSnd, depth + 1) + case array: ArrayType[a] => + attachResult("ArrayType", depth) + decomposeValue(array.tA, depth + 1) + case opt: OptionType[a] => + attachResult("OptionType", depth) + decomposeValue(opt.tA, depth + 1) + case coll: CollType[a] => + attachResult("CollType", depth) + decomposeValue(coll.tItem, depth + 1) + case replColl: ReplCollType[a] => + attachResult("ReplCollType", depth) + decomposeValue(replColl.tItem, depth + 1) + case (stringType: RType[String]@unchecked) => attachResult("StringType", depth) + case _ => throw new RuntimeException(s"Unknown generated RType: ${item}") + } +} diff --git a/library/src/test/scala/scalan/RTypeGens.scala b/library/src/test/scala/scalan/RTypeGens.scala new file mode 100644 index 000000000..5b83a55af --- /dev/null +++ b/library/src/test/scala/scalan/RTypeGens.scala @@ -0,0 +1,120 @@ +package scalan + +import org.scalacheck.{Arbitrary, Gen} + +trait RTypeGens { + import Gen._ + import RType._ + import special.collection._ + + /* + * There're three generators for primitive types, since from one side, we want to have just numeric/char types, + * sometimes there's a need to put UnitType (seldom), sometimes not, that's why additional generator has been made. + */ + val primitiveTypeGen = Gen.oneOf[RType[_]](BooleanType, ByteType, ShortType, + IntType, LongType, CharType, FloatType, DoubleType) + + val primitiveTypeWithUnitGen = Gen.oneOf[RType[_]](primitiveTypeGen, UnitType) + + // The reason why StringType is distiguished is that in type hierarchy StringType consists of Chars + val dataTypeGen = Gen.oneOf[RType[_]](primitiveTypeGen, StringType) + + def pairTypeGen(itemGen: Gen[RType[_]], depth: Int): Gen[PairType[_, _]] = { + def pairTypeGenFinal(itemGenLeft: Gen[RType[_]], itemGenRight: Gen[RType[_]]): Gen[PairType[_, _]] = { + for { left <- itemGenLeft; right <- itemGenRight } yield new PairType(left, right) + } + depth match { + case 1 => + pairTypeGenFinal(itemGen, itemGen) + case _ => + val lg = pairTypeGen(itemGen, depth - 1) + val rg = pairTypeGen(itemGen, depth - 1) + Gen.oneOf( + pairTypeGenFinal(itemGen, itemGen), + pairTypeGenFinal(lg, itemGen), + pairTypeGenFinal(itemGen, rg), + pairTypeGenFinal(lg, rg), + ) + } + } + + def arrayTypeGen(itemGen: Gen[RType[_]], depth: Int): Gen[ArrayType[_]] = { + def arrayTypeGenFinal(itemGen: Gen[RType[_]]): Gen[ArrayType[_]] = { + for { item <- itemGen } yield new ArrayType(item) + } + depth match { + case 1 => + arrayTypeGenFinal(itemGen) + case _ => + Gen.oneOf( + arrayTypeGenFinal(itemGen), + arrayTypeGenFinal(arrayTypeGen(itemGen, depth - 1)) + ) + } + } + + def getFullTypeGen(depth: Int): Gen[RType[_]] = depth match { + case 1 => Gen.oneOf(dataTypeGen, pairTypeGen(dataTypeGen, 1), arrayTypeGen(dataTypeGen, 1)) + case _ => + Gen.oneOf(dataTypeGen, pairTypeGen(getFullTypeGen(depth - 1), 1), arrayTypeGen(getFullTypeGen(depth - 1), 1)) + } + + def getArrayGen(depth: Int) = arrayTypeGen(getFullTypeGen(depth - 1), 1) + + def getPairGen(depth: Int) = pairTypeGen(getFullTypeGen(depth - 1), 1) + + def optionTypeGen(itemGen: Gen[RType[_]], depth: Int): Gen[OptionType[_]] = { + def optionTypeGenFinal(itemGen: Gen[RType[_]]): Gen[OptionType[_]] = { + for {item <- itemGen } yield new OptionType(item) + } + depth match { + case 1 => + optionTypeGenFinal(itemGen) + case _ => + Gen.oneOf( + optionTypeGenFinal(itemGen), + optionTypeGenFinal(optionTypeGen(itemGen, depth - 1)) + ) + } + } + + def collTypeGen(itemGen: Gen[RType[_]], depth: Int): Gen[CollType[_]] = { + def collTypeGenFinal(itemGen: Gen[RType[_]]): Gen[CollType[_]] = { + for { item <- itemGen } yield new CollType(item) + } + depth match { + case 1 => + collTypeGenFinal(itemGen) + case _ => + Gen.oneOf( + collTypeGenFinal(itemGen), + collTypeGenFinal(collTypeGen(itemGen, depth - 1)) + ) + } + } + + def replCollTypeGen(itemGen: Gen[RType[_]], depth: Int): Gen[ReplCollType[_]] = { + def replCollTypeGenFinal(itemGen: Gen[RType[_]]): Gen[ReplCollType[_]] = { + for { item <- itemGen } yield new ReplCollType(item) + } + depth match { + case 1 => + replCollTypeGenFinal(itemGen) + case _ => + Gen.oneOf( + replCollTypeGenFinal(itemGen), + replCollTypeGenFinal(replCollTypeGen(itemGen, depth - 1)) + ) + } + } + + def extendedTypeGen(depth: Int): Gen[RType[_]] = depth match { + case 1 => Gen.oneOf(dataTypeGen, pairTypeGen(dataTypeGen, 1), optionTypeGen(dataTypeGen, 1), + arrayTypeGen(dataTypeGen, 1), collTypeGen(dataTypeGen, 1), replCollTypeGen(dataTypeGen, 1)) + case _ => + Gen.oneOf(dataTypeGen, pairTypeGen(extendedTypeGen(depth - 1), 1), optionTypeGen(extendedTypeGen(depth - 1), 1), + arrayTypeGen(extendedTypeGen(depth - 1), 1), collTypeGen(extendedTypeGen(depth - 1), 1), + replCollTypeGen(extendedTypeGen(depth - 1), 1) + ) + } +} diff --git a/library/src/test/scala/scalan/RTypeTests.scala b/library/src/test/scala/scalan/RTypeTests.scala new file mode 100644 index 000000000..6eb90626a --- /dev/null +++ b/library/src/test/scala/scalan/RTypeTests.scala @@ -0,0 +1,26 @@ +package scalan + +import org.scalacheck.Gen +import org.scalatest.{Matchers, PropSpec} +import org.scalatest.prop.PropertyChecks + +import spire.syntax.all._ + +class RTypeTests extends PropSpec with PropertyChecks with Matchers with RTypeGens { + testSuite => + + import Gen._ + import RType._ + + val typeGenDepth = 10 + + property("RType FullTypeGen coverage") { + val minSuccess = MinSuccessful(300) + + val typeCoverageChecker = new FullTypeCoverageChecker() + forAll(extendedTypeGen(typeGenDepth), minSuccess) { t: RType[_] => + typeCoverageChecker.consider(t) + } + typeCoverageChecker.isFullyCovered(typeGenDepth) shouldBe true + } +} From 3decc9a8d711f2ea1b8741b618fad94e0e4449c6 Mon Sep 17 00:00:00 2001 From: Eugene Gostkin Date: Sun, 11 Aug 2019 15:34:52 +0300 Subject: [PATCH 11/13] Implement value generators (#45) * Implement generators * Generate values by RType * Typos fix * Remove unneeded code * Update comments * Simplify implementation * Some fixes * Enhance generators * Add coverage test for FullTypeGenerator * Update test name, move RTypeGenCoverageChecker to new file * Cover value generator with test * Refactoring * Cosmetics * Review comments fix, add OptionType and CollType support * Make CollType and OptionType generators * Fixes according review * Remove prints * Fix comments --- .../scalan/RTypeGenCoverageChecker.scala | 42 ++++++++---- library/src/test/scala/scalan/RTypeGens.scala | 64 +++++++++++++++++++ .../src/test/scala/scalan/RTypeTestUtil.scala | 50 +++++++++++++++ .../src/test/scala/scalan/RTypeTests.scala | 19 +++++- .../scala/special/collections/CollGens.scala | 2 +- 5 files changed, 160 insertions(+), 17 deletions(-) create mode 100644 library/src/test/scala/scalan/RTypeTestUtil.scala diff --git a/library/src/test/scala/scalan/RTypeGenCoverageChecker.scala b/library/src/test/scala/scalan/RTypeGenCoverageChecker.scala index 3634f43f4..9d92556ac 100644 --- a/library/src/test/scala/scalan/RTypeGenCoverageChecker.scala +++ b/library/src/test/scala/scalan/RTypeGenCoverageChecker.scala @@ -2,8 +2,22 @@ package scalan import spire.syntax.all.cfor +/** + * In future we could have some other RType generators, + * so we will to unify coverage checks through this trait. + */ trait RTypeGenCoverageChecker { + /** Take type into consideration. + * + * @param item RType[_] value to be marked as generated + */ def consider(item: RType[_]): Unit + + /** Check if all required types are generated. + * + * @param depth There could be nested types. This parameter show how deep this nesting should be checked. + * @return `true` if every required types has been generated, `false` otherwise. + */ def isFullyCovered(depth: Int): Boolean } @@ -11,7 +25,7 @@ class FullTypeCoverageChecker extends RTypeGenCoverageChecker { import RType._ import special.collection._ - private type TypePosition = (String, Int) + private type TypePosition = (Class[_], Int) private var typePositions: Set[TypePosition] = Set.empty override def consider(item: RType[_]): Unit = { @@ -19,12 +33,12 @@ class FullTypeCoverageChecker extends RTypeGenCoverageChecker { } override def isFullyCovered(depth: Int): Boolean = { - val names = Seq("PrimitiveType", "PairType", "ArrayType", "StringType", "CollType", "ReplCollType", "OptionType") + val typesForCoverage = Seq(classOf[PrimitiveType[_]], classOf[PairType[_, _]], classOf[ArrayType[_]], + classOf[CollType[_]], classOf[ReplCollType[_]], classOf[OptionType[_]], StringType.classTag.getClass) cfor(0)(_ < depth, _ + 1) { i => - for (name <- names) { - - if (!typePositions.contains(new TypePosition(name, i))) { - println(s"Type ${name} is not found at depth ${i}") + for (currentType <- typesForCoverage) { + if (!typePositions.contains(new TypePosition(currentType, i))) { + println(s"Type ${currentType} is not found at depth ${i}") return false } } @@ -32,30 +46,30 @@ class FullTypeCoverageChecker extends RTypeGenCoverageChecker { true } - private def attachResult(typeName: String, depth: Int) = { + private def attachResult(typeName: Class[_], depth: Int) = { val newTypePosition: TypePosition = (typeName, depth) typePositions = typePositions + newTypePosition } private def decomposeValue(item: RType[_], depth: Int = 0): Unit = item match { - case prim: PrimitiveType[a] => attachResult("PrimitiveType", depth) + case prim: PrimitiveType[a] => attachResult(classOf[PrimitiveType[_]], depth) case pair: PairType[a, b]@unchecked => - attachResult("PairType", depth) + attachResult(classOf[PairType[_, _]], depth) decomposeValue(pair.tFst, depth + 1) decomposeValue(pair.tSnd, depth + 1) case array: ArrayType[a] => - attachResult("ArrayType", depth) + attachResult(classOf[ArrayType[_]], depth) decomposeValue(array.tA, depth + 1) case opt: OptionType[a] => - attachResult("OptionType", depth) + attachResult(classOf[OptionType[_]], depth) decomposeValue(opt.tA, depth + 1) case coll: CollType[a] => - attachResult("CollType", depth) + attachResult(classOf[CollType[_]], depth) decomposeValue(coll.tItem, depth + 1) case replColl: ReplCollType[a] => - attachResult("ReplCollType", depth) + attachResult(classOf[ReplCollType[_]], depth) decomposeValue(replColl.tItem, depth + 1) - case (stringType: RType[String]@unchecked) => attachResult("StringType", depth) + case StringType => attachResult(StringType.classTag.getClass, depth) case _ => throw new RuntimeException(s"Unknown generated RType: ${item}") } } diff --git a/library/src/test/scala/scalan/RTypeGens.scala b/library/src/test/scala/scalan/RTypeGens.scala index 5b83a55af..7622d5f7e 100644 --- a/library/src/test/scala/scalan/RTypeGens.scala +++ b/library/src/test/scala/scalan/RTypeGens.scala @@ -1,7 +1,10 @@ package scalan +import org.scalacheck.util.Buildable import org.scalacheck.{Arbitrary, Gen} +class GenConfiguration(val maxArrayLength: Int = 100) {} + trait RTypeGens { import Gen._ import RType._ @@ -117,4 +120,65 @@ trait RTypeGens { replCollTypeGen(extendedTypeGen(depth - 1), 1) ) } + + def primitiveValueGen[T: RType](t: PrimitiveType[T]): Gen[T] = t match { + case ByteType => choose[Byte](Byte.MinValue, Byte.MaxValue).asInstanceOf[Gen[T]] + case ShortType => choose[Short](Short.MinValue, Short.MaxValue).asInstanceOf[Gen[T]] + case IntType => choose[Int](Int.MinValue, Int.MaxValue).asInstanceOf[Gen[T]] + case CharType => choose[Char](Char.MinValue, Char.MaxValue).asInstanceOf[Gen[T]] + case LongType => choose[Long](Long.MinValue, Long.MaxValue).asInstanceOf[Gen[T]] + case FloatType => choose[Float](Float.MinValue, Float.MaxValue).asInstanceOf[Gen[T]] + case DoubleType => choose[Double](Double.MinValue, Double.MaxValue).asInstanceOf[Gen[T]] + case BooleanType => Gen.oneOf(true, false).asInstanceOf[Gen[T]] + case _ => throw new RuntimeException(s"Can't interpret ${t} as non-unit primitive type.") + } + + val builder: CollBuilder = new CollOverArrayBuilder + + def getArrayGen[T](valGen: Gen[T], count: Int)(implicit t: RType[T]): Gen[Array[T]] = { + containerOfN[Array, T](count, valGen) + } + + def getCollOverArrayGen[T: RType](valGen: Gen[T], count: Int): Gen[Coll[T]] = { + getArrayGen(valGen, count).map(builder.fromArray(_)) + } + + def getCollReplGen[T: RType](valGen: Gen[T], count: Int): Gen[Coll[T]] = { + for { l <- choose(0, count); v <- valGen } yield new CReplColl(v, l) + } + + def getCollViewGen[A: RType](valGen: Gen[Coll[A]]): Gen[Coll[A]] = { + valGen.map(builder.makeView(_, identity[A])) + } + + def getCollViewGen[A, B: RType](valGen: Gen[Coll[A]], f: A => B): Gen[Coll[B]] = { + valGen.map(builder.makeView(_, f)) + } + + def rtypeValueGen[T](conf: GenConfiguration)(implicit t: RType[T]): Gen[T] = t match { + case prim: PrimitiveType[a] => + primitiveValueGen(prim)(prim) + case arrayType: ArrayType[a] => + getArrayGen(rtypeValueGen(conf)(arrayType.tA).asInstanceOf[Gen[a]], conf.maxArrayLength)(arrayType.tA) + case pairType: PairType[a, b] => + for { left <- rtypeValueGen(conf)(pairType.tFst); right <- rtypeValueGen(conf)(pairType.tSnd) } + yield (left.asInstanceOf[a], right.asInstanceOf[b]) + case StringType => + Gen.asciiPrintableStr + case collType: CollType[a] => collType.tItem match { + case pairType: PairType[fst, snd] => + val tA = pairType.tFst + val tB = pairType.tSnd + for { + left <- getCollOverArrayGen(rtypeValueGen(conf)(tA), conf.maxArrayLength)(tA); + right <- getCollOverArrayGen(rtypeValueGen(conf)(tB), conf.maxArrayLength)(tB) + } yield new PairOfCols(left, right) + case _ => getCollOverArrayGen(rtypeValueGen(conf)(collType.tItem), conf.maxArrayLength)(collType.tItem) + } + case replCollType: ReplCollType[a] => + getCollReplGen(rtypeValueGen(conf)(replCollType.tItem), conf.maxArrayLength)(replCollType.tItem).asInstanceOf[Gen[T]] + case optionType: OptionType[a] => + Gen.option(rtypeValueGen(conf)(optionType.tA)) + case _ => throw new RuntimeException(s"Can't create generator for ${t}: this type is still not supported.") + } } diff --git a/library/src/test/scala/scalan/RTypeTestUtil.scala b/library/src/test/scala/scalan/RTypeTestUtil.scala new file mode 100644 index 000000000..c36dd4865 --- /dev/null +++ b/library/src/test/scala/scalan/RTypeTestUtil.scala @@ -0,0 +1,50 @@ +package scalan + +import special.collection.CollType +import spire.syntax.all._ + +object RTypeTestUtil { + import scalan.RType._ + import special.collection._ + + def valueMatchesRType[T](value: T, tA: RType[_]): Boolean = tA match { + case prim: PrimitiveType[a] => value match { + case b: Byte => prim == ByteType + case b: Short => prim == ShortType + case b: Int => prim == IntType + case b: Long => prim == LongType + case b: Char => prim == CharType + case b: Float => prim == FloatType + case b: Double => prim == DoubleType + case b: Boolean => prim == BooleanType + case b: Unit => prim == UnitType + case _ => false + } + case arrayType: ArrayType[a] => value match { + case arr: Array[_] => arr.forall(item => valueMatchesRType(item, arrayType.tA)) + case _ => false + } + case pairType: PairType[a, b] => value match { + case pair: Tuple2[_, _] => valueMatchesRType(pair._1, pairType.tFst) && valueMatchesRType(pair._2, pairType.tSnd) + case _ => false + } + case StringType => value match { + case str: String => true + case _ => false + } + case collType: CollType[a] => value match { + case coll: Coll[_] => coll.tItem == collType.tItem && coll.forall(item => valueMatchesRType(item, collType.tItem)) + case _ => false + } + case replCollType: ReplCollType[a] => value match { + case coll: ReplColl[_] => coll.tItem == replCollType.tItem && coll.forall(item => valueMatchesRType(item, replCollType.tItem)) + case _ => false + } + case optionType: OptionType[a] => value match { + case op: Option[_] => op.forall(item => valueMatchesRType(item, optionType.tA)) + case _ => false + } + + case _ => false + } +} diff --git a/library/src/test/scala/scalan/RTypeTests.scala b/library/src/test/scala/scalan/RTypeTests.scala index 6eb90626a..a998e8ad9 100644 --- a/library/src/test/scala/scalan/RTypeTests.scala +++ b/library/src/test/scala/scalan/RTypeTests.scala @@ -1,6 +1,7 @@ package scalan import org.scalacheck.Gen +import org.scalactic.anyvals.PosInt import org.scalatest.{Matchers, PropSpec} import org.scalatest.prop.PropertyChecks @@ -12,10 +13,14 @@ class RTypeTests extends PropSpec with PropertyChecks with Matchers with RTypeGe import Gen._ import RType._ - val typeGenDepth = 10 + val typeGenDepth = 5 + val coverageThreshold = 100 + + val testConfiguration = new GenConfiguration(maxArrayLength = 10) + def extendedValueGen[T](t: RType[T]): Gen[T] = rtypeValueGen(testConfiguration)(t) property("RType FullTypeGen coverage") { - val minSuccess = MinSuccessful(300) + val minSuccess = MinSuccessful(PosInt.from(coverageThreshold).get) val typeCoverageChecker = new FullTypeCoverageChecker() forAll(extendedTypeGen(typeGenDepth), minSuccess) { t: RType[_] => @@ -23,4 +28,14 @@ class RTypeTests extends PropSpec with PropertyChecks with Matchers with RTypeGe } typeCoverageChecker.isFullyCovered(typeGenDepth) shouldBe true } + + property("RType generate value by type") { + import scala.runtime.ScalaRunTime._ + val minSuccess = MinSuccessful(PosInt.from(coverageThreshold).get) + forAll(extendedTypeGen(typeGenDepth), minSuccess) { t: RType[_] => + forAll(extendedValueGen(t)) { value => + RTypeTestUtil.valueMatchesRType(value, t) shouldBe true + } + } + } } diff --git a/library/src/test/scala/special/collections/CollGens.scala b/library/src/test/scala/special/collections/CollGens.scala index b77e4d305..19bd19ad0 100644 --- a/library/src/test/scala/special/collections/CollGens.scala +++ b/library/src/test/scala/special/collections/CollGens.scala @@ -16,11 +16,11 @@ trait CollGens { testSuite => val builder: CollBuilder = new CollOverArrayBuilder val monoid = builder.Monoids.intPlusMonoid val valGen = choose(-100, 100) - val byteGen = choose[Byte](-100, 100) val indexGen = choose(0, 100) val replacedGen = choose(0, 100) val lenGen = choose(0, 100) + val byteGen = choose[Byte](Byte.MinValue, Byte.MaxValue) val shortGen = choose[Short](Short.MinValue, Short.MaxValue) val intGen = choose[Int](Int.MinValue, Int.MaxValue) val longGen = choose[Long](Long.MinValue, Long.MaxValue) From 482873b22e96decdbec9acb9ca11863745cbf997 Mon Sep 17 00:00:00 2001 From: Eugene Gostkin Date: Mon, 12 Aug 2019 14:58:44 +0300 Subject: [PATCH 12/13] Issue on data clone (#46) * Implement generators * Generate values by RType * Typos fix * Remove unneeded code * Update comments * Simplify implementation * Some fixes * Enhance generators * Add coverage test for FullTypeGenerator * Update test name, move RTypeGenCoverageChecker to new file * Cover value generator with test * Towards implementation * Some work * Small fix * Implement cloning * Refactoring * Cosmetics * Review comments fix, add OptionType and CollType support * Make CollType and OptionType generators * Towards implementation * Small fix * Implement cloning * Add Coll and Option support * Fixes according review * Remove prints * Test cloning * Remove printing * Merge diverted branches * Small fix * Specify types * Finish with type inference * Fix comments * Fixes according the review * Fix naming --- .../src/main/scala/scalan/RTypeUtil.scala | 39 +++++++++++++++++++ .../src/test/scala/scalan/RTypeTestUtil.scala | 39 +++++++++++++++++++ .../src/test/scala/scalan/RTypeTests.scala | 24 +++++++++++- 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 library-impl/src/main/scala/scalan/RTypeUtil.scala diff --git a/library-impl/src/main/scala/scalan/RTypeUtil.scala b/library-impl/src/main/scala/scalan/RTypeUtil.scala new file mode 100644 index 000000000..11fc567b7 --- /dev/null +++ b/library-impl/src/main/scala/scalan/RTypeUtil.scala @@ -0,0 +1,39 @@ +package scalan + +import spire.syntax.all._ +import spire.syntax.trig.trigOps + +object RTypeUtil { + import scalan.RType._ + import special.collection._ + + def clone[T](value: T)(implicit t: RType[T]): T = t match { + case prim: PrimitiveType[a] => value + case arrayType: ArrayType[a] => + val array = value.asInstanceOf[Array[a]] + var copy = Array.ofDim[a](array.length)(arrayType.tA.classTag) + cfor(0)(_ < array.length, _ + 1) { i => + copy(i) = clone(array(i))(arrayType.tA).asInstanceOf[a] + } + copy.asInstanceOf[T] + case pairType: PairType[a, b] => + val pair = value.asInstanceOf[Tuple2[a, b]] + return (clone(pair._1)(pairType.tFst), clone(pair._2)(pairType.tSnd)).asInstanceOf[T] + case optionType: OptionType[a] => + val option = value.asInstanceOf[Option[a]] + val cloned = if (option.isDefined) Some(clone(option.get)(optionType.tA)) else None + cloned.asInstanceOf[T] + case replCollType: ReplCollType[a] => + val coll = value.asInstanceOf[ReplColl[a]] + val cloned = clone(coll.value)(replCollType.tItem) + (new CReplColl(cloned, coll.length)(replCollType.tItem)).asInstanceOf[T] + case collType: CollType[a] => + val coll = value.asInstanceOf[Coll[a]] + val cloned = clone(coll.toArray)(ArrayType(collType.tItem)) + coll.builder.fromArray(cloned)(collType.tItem).asInstanceOf[T] + case StringType => + val arr = value.asInstanceOf[String].toArray + arr.mkString.asInstanceOf[T] + case _ => throw new RuntimeException(s"Can't clone ${t}.") + } +} diff --git a/library/src/test/scala/scalan/RTypeTestUtil.scala b/library/src/test/scala/scalan/RTypeTestUtil.scala index c36dd4865..2895a9a9e 100644 --- a/library/src/test/scala/scalan/RTypeTestUtil.scala +++ b/library/src/test/scala/scalan/RTypeTestUtil.scala @@ -47,4 +47,43 @@ object RTypeTestUtil { case _ => false } + + def deepEqualityChecker[A](value: A, copy: A)(implicit tA: RType[A]): Boolean = tA match { + case arrayType: ArrayType[a] => + val valInstance = value.asInstanceOf[Array[a]] + val copyInstance = copy.asInstanceOf[Array[a]] + if (valInstance.length != copyInstance.length) return false + cfor(0)(_ < valInstance.length, _ + 1) { i => + if (!deepEqualityChecker(valInstance(i), copyInstance(i))(arrayType.tA)) + return false + } + true + case prim: PrimitiveType[a] => + copy == value + case pairType: PairType[a, b] => + val valInstance = value.asInstanceOf[Tuple2[a, b]] + val copyInstance = copy.asInstanceOf[Tuple2[a, b]] + deepEqualityChecker(valInstance._1, copyInstance._1)(pairType.tFst) && deepEqualityChecker(valInstance._2, copyInstance._2)(pairType.tSnd) + case StringType => + copy == value + case opt: OptionType[a] => + val copyOpt = copy.asInstanceOf[Option[a]] + val valueOpt = value.asInstanceOf[Option[a]] + if (copyOpt.isDefined != valueOpt.isDefined) return false + if (copyOpt.isDefined) deepEqualityChecker(copyOpt.get, valueOpt.get)(opt.tA) else true + case coll: ReplCollType[a] => + val copyColl = copy.asInstanceOf[ReplColl[a]] + val valueColl = value.asInstanceOf[ReplColl[a]] + copyColl.length == valueColl.length && deepEqualityChecker(valueColl.value, copyColl.value)(coll.tItem) + case coll: CollType[a] => + val copyColl = copy.asInstanceOf[Coll[a]] + val valueColl = value.asInstanceOf[Coll[a]] + if (copyColl.length != valueColl.length) return false + cfor(0)(_ < valueColl.length, _ + 1) { i => + if (!deepEqualityChecker(valueColl(i), copyColl(i))(coll.tItem)) + return false + } + true + case _ => copy == value + } } diff --git a/library/src/test/scala/scalan/RTypeTests.scala b/library/src/test/scala/scalan/RTypeTests.scala index a998e8ad9..d6015f3cf 100644 --- a/library/src/test/scala/scalan/RTypeTests.scala +++ b/library/src/test/scala/scalan/RTypeTests.scala @@ -4,7 +4,7 @@ import org.scalacheck.Gen import org.scalactic.anyvals.PosInt import org.scalatest.{Matchers, PropSpec} import org.scalatest.prop.PropertyChecks - +import scalan.RTypeTestUtil.deepEqualityChecker import spire.syntax.all._ class RTypeTests extends PropSpec with PropertyChecks with Matchers with RTypeGens { @@ -12,6 +12,7 @@ class RTypeTests extends PropSpec with PropertyChecks with Matchers with RTypeGe import Gen._ import RType._ + import special.collection._ val typeGenDepth = 5 val coverageThreshold = 100 @@ -38,4 +39,25 @@ class RTypeTests extends PropSpec with PropertyChecks with Matchers with RTypeGe } } } + + property("RType clone value") { + def checkCopy[T](value: T, tA: RType[T]): Unit = { + val copy: T = RTypeUtil.clone(value)(tA) + + deepEqualityChecker(copy, value)(tA) shouldBe true + val haveDifferentAddresses = tA match { + case prim: PrimitiveType[a] => true + case optionType: OptionType[a] if value.asInstanceOf[Option[a]].isEmpty => true + case _ => !(copy.asInstanceOf[AnyRef] eq value.asInstanceOf[AnyRef]) + } + haveDifferentAddresses shouldBe true + } + + val minSuccess = MinSuccessful(PosInt.from(coverageThreshold).get) + forAll(extendedTypeGen(typeGenDepth), minSuccess) { t => + forAll(extendedValueGen(t)) { value => + checkCopy(value, t.asInstanceOf[RType[Any]]) + } + } + } } From f0e12df76d85d2298f070237cd384717b6eaea48 Mon Sep 17 00:00:00 2001 From: Eugene Gostkin Date: Fri, 13 Sep 2019 12:03:48 +0300 Subject: [PATCH 13/13] Test colls with new generators (#47) * Implement generators * Generate values by RType * Typos fix * Remove unneeded code * Update comments * Simplify implementation * Some fixes * Enhance generators * Add coverage test for FullTypeGenerator * Update test name, move RTypeGenCoverageChecker to new file * Cover value generator with test * Towards implementation * Some work * Small fix * Implement cloning * Refactoring * Cosmetics * Review comments fix, add OptionType and CollType support * Make CollType and OptionType generators * Towards implementation * Small fix * Implement cloning * Add Coll and Option support * Fixes according review * Remove prints * Test cloning * Remove printing * Merge diverted branches * Small fix * Specify types * Finish with type inference * Fix comments * Towards implementation * WIP * Fixes according the review * Fix naming * WIP * Add a comment * Refactor tests * Code fixes, simplification --- .../special/collection/CollsOverArrays.scala | 2 + library/src/test/scala/scalan/RTypeGens.scala | 65 +- .../src/test/scala/scalan/RTypeTests.scala | 6 +- .../scala/special/collections/CollGens.scala | 134 +---- .../special/collections/CollsTests.scala | 565 +++++++++--------- 5 files changed, 348 insertions(+), 424 deletions(-) diff --git a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala index 1ebfd641d..cdd9a8960 100644 --- a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala +++ b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala @@ -684,6 +684,8 @@ class CReplColl[@specialized A](val value: A, val length: Int)(implicit tA: RTyp @NeverInline override def updated(index: Int, elem: A): Coll[A] = { + if (index < 0 || index >= length) + throw new IndexOutOfBoundsException if (elem == value) this else { val res = toArray.updated(index, elem) diff --git a/library/src/test/scala/scalan/RTypeGens.scala b/library/src/test/scala/scalan/RTypeGens.scala index 7622d5f7e..449df708c 100644 --- a/library/src/test/scala/scalan/RTypeGens.scala +++ b/library/src/test/scala/scalan/RTypeGens.scala @@ -1,9 +1,17 @@ package scalan -import org.scalacheck.util.Buildable import org.scalacheck.{Arbitrary, Gen} -class GenConfiguration(val maxArrayLength: Int = 100) {} +class GenConfiguration( + val maxArrayLength: Int = 100, + val byteBorders: (Byte, Byte) = (Byte.MinValue, Byte.MaxValue), + val shortBorders: (Short, Short) = (Short.MinValue, Short.MaxValue), + val intBorders: (Int, Int) = (Int.MinValue, Int.MaxValue), + val longBorders: (Long, Long) = (Long.MinValue, Long.MaxValue), + val charBorders: (Char, Char) = (Char.MinValue, Char.MaxValue), + val floatBorders: (Float, Float) = (Float.MinValue, Float.MaxValue), + val doubleBorders: (Double, Double) = (Double.MinValue, Double.MaxValue) + ) trait RTypeGens { import Gen._ @@ -19,9 +27,15 @@ trait RTypeGens { val primitiveTypeWithUnitGen = Gen.oneOf[RType[_]](primitiveTypeGen, UnitType) - // The reason why StringType is distiguished is that in type hierarchy StringType consists of Chars + // The reason why StringType is distinguished is that in type hierarchy StringType consists of Chars val dataTypeGen = Gen.oneOf[RType[_]](primitiveTypeGen, StringType) + def checkDepth(depth: Int): Unit = { + if (depth <= 0) { + throw new RuntimeException(s"Generation depth should be positive, found ${depth}") + } + } + def pairTypeGen(itemGen: Gen[RType[_]], depth: Int): Gen[PairType[_, _]] = { def pairTypeGenFinal(itemGenLeft: Gen[RType[_]], itemGenRight: Gen[RType[_]]): Gen[PairType[_, _]] = { for { left <- itemGenLeft; right <- itemGenRight } yield new PairType(left, right) @@ -30,6 +44,7 @@ trait RTypeGens { case 1 => pairTypeGenFinal(itemGen, itemGen) case _ => + checkDepth(depth) val lg = pairTypeGen(itemGen, depth - 1) val rg = pairTypeGen(itemGen, depth - 1) Gen.oneOf( @@ -49,6 +64,7 @@ trait RTypeGens { case 1 => arrayTypeGenFinal(itemGen) case _ => + checkDepth(depth) Gen.oneOf( arrayTypeGenFinal(itemGen), arrayTypeGenFinal(arrayTypeGen(itemGen, depth - 1)) @@ -59,6 +75,7 @@ trait RTypeGens { def getFullTypeGen(depth: Int): Gen[RType[_]] = depth match { case 1 => Gen.oneOf(dataTypeGen, pairTypeGen(dataTypeGen, 1), arrayTypeGen(dataTypeGen, 1)) case _ => + checkDepth(depth) Gen.oneOf(dataTypeGen, pairTypeGen(getFullTypeGen(depth - 1), 1), arrayTypeGen(getFullTypeGen(depth - 1), 1)) } @@ -74,6 +91,7 @@ trait RTypeGens { case 1 => optionTypeGenFinal(itemGen) case _ => + checkDepth(depth) Gen.oneOf( optionTypeGenFinal(itemGen), optionTypeGenFinal(optionTypeGen(itemGen, depth - 1)) @@ -89,6 +107,7 @@ trait RTypeGens { case 1 => collTypeGenFinal(itemGen) case _ => + checkDepth(depth) Gen.oneOf( collTypeGenFinal(itemGen), collTypeGenFinal(collTypeGen(itemGen, depth - 1)) @@ -104,6 +123,7 @@ trait RTypeGens { case 1 => replCollTypeGenFinal(itemGen) case _ => + checkDepth(depth) Gen.oneOf( replCollTypeGenFinal(itemGen), replCollTypeGenFinal(replCollTypeGen(itemGen, depth - 1)) @@ -111,24 +131,37 @@ trait RTypeGens { } } - def extendedTypeGen(depth: Int): Gen[RType[_]] = depth match { + def rtypeGen(depth: Int): Gen[RType[_]] = depth match { case 1 => Gen.oneOf(dataTypeGen, pairTypeGen(dataTypeGen, 1), optionTypeGen(dataTypeGen, 1), arrayTypeGen(dataTypeGen, 1), collTypeGen(dataTypeGen, 1), replCollTypeGen(dataTypeGen, 1)) case _ => - Gen.oneOf(dataTypeGen, pairTypeGen(extendedTypeGen(depth - 1), 1), optionTypeGen(extendedTypeGen(depth - 1), 1), - arrayTypeGen(extendedTypeGen(depth - 1), 1), collTypeGen(extendedTypeGen(depth - 1), 1), - replCollTypeGen(extendedTypeGen(depth - 1), 1) + checkDepth(depth) + Gen.oneOf(dataTypeGen, pairTypeGen(rtypeGen(depth - 1), 1), optionTypeGen(rtypeGen(depth - 1), 1), + arrayTypeGen(rtypeGen(depth - 1), 1), collTypeGen(rtypeGen(depth - 1), 1), + replCollTypeGen(rtypeGen(depth - 1), 1) ) } - def primitiveValueGen[T: RType](t: PrimitiveType[T]): Gen[T] = t match { - case ByteType => choose[Byte](Byte.MinValue, Byte.MaxValue).asInstanceOf[Gen[T]] - case ShortType => choose[Short](Short.MinValue, Short.MaxValue).asInstanceOf[Gen[T]] - case IntType => choose[Int](Int.MinValue, Int.MaxValue).asInstanceOf[Gen[T]] - case CharType => choose[Char](Char.MinValue, Char.MaxValue).asInstanceOf[Gen[T]] - case LongType => choose[Long](Long.MinValue, Long.MaxValue).asInstanceOf[Gen[T]] - case FloatType => choose[Float](Float.MinValue, Float.MaxValue).asInstanceOf[Gen[T]] - case DoubleType => choose[Double](Double.MinValue, Double.MaxValue).asInstanceOf[Gen[T]] + def collTypeGen(depth: Int): Gen[RType[Coll[_]]] = { + checkDepth(depth) + val innerGen = rtypeGen(depth - 1) + Gen.oneOf(collTypeGen(innerGen, 1).asInstanceOf[Gen[RType[Coll[_]]]], + replCollTypeGen(innerGen, 1).asInstanceOf[Gen[RType[Coll[_]]]]) + } + + def collTypeGen[T](itemGen: Gen[RType[T]]): Gen[RType[Coll[T]]] = { + Gen.oneOf(collTypeGen(itemGen, 1).asInstanceOf[Gen[RType[Coll[T]]]], + replCollTypeGen(itemGen, 1).asInstanceOf[Gen[RType[Coll[T]]]]) + } + + def primitiveValueGen[T](conf: GenConfiguration)(implicit t: PrimitiveType[T]): Gen[T] = t match { + case ByteType => choose[Byte](conf.byteBorders._1, conf.byteBorders._2).asInstanceOf[Gen[T]] + case ShortType => choose[Short](conf.shortBorders._1, conf.shortBorders._2).asInstanceOf[Gen[T]] + case IntType => choose[Int](conf.intBorders._1, conf.intBorders._2).asInstanceOf[Gen[T]] + case CharType => choose[Char](conf.charBorders._1, conf.charBorders._2).asInstanceOf[Gen[T]] + case LongType => choose[Long](conf.longBorders._1, conf.longBorders._2).asInstanceOf[Gen[T]] + case FloatType => choose[Float](conf.floatBorders._1, conf.floatBorders._2).asInstanceOf[Gen[T]] + case DoubleType => choose[Double](conf.doubleBorders._1, conf.doubleBorders._2).asInstanceOf[Gen[T]] case BooleanType => Gen.oneOf(true, false).asInstanceOf[Gen[T]] case _ => throw new RuntimeException(s"Can't interpret ${t} as non-unit primitive type.") } @@ -157,7 +190,7 @@ trait RTypeGens { def rtypeValueGen[T](conf: GenConfiguration)(implicit t: RType[T]): Gen[T] = t match { case prim: PrimitiveType[a] => - primitiveValueGen(prim)(prim) + primitiveValueGen(conf)(prim) case arrayType: ArrayType[a] => getArrayGen(rtypeValueGen(conf)(arrayType.tA).asInstanceOf[Gen[a]], conf.maxArrayLength)(arrayType.tA) case pairType: PairType[a, b] => diff --git a/library/src/test/scala/scalan/RTypeTests.scala b/library/src/test/scala/scalan/RTypeTests.scala index d6015f3cf..823615ec9 100644 --- a/library/src/test/scala/scalan/RTypeTests.scala +++ b/library/src/test/scala/scalan/RTypeTests.scala @@ -24,7 +24,7 @@ class RTypeTests extends PropSpec with PropertyChecks with Matchers with RTypeGe val minSuccess = MinSuccessful(PosInt.from(coverageThreshold).get) val typeCoverageChecker = new FullTypeCoverageChecker() - forAll(extendedTypeGen(typeGenDepth), minSuccess) { t: RType[_] => + forAll(rtypeGen(typeGenDepth), minSuccess) { t: RType[_] => typeCoverageChecker.consider(t) } typeCoverageChecker.isFullyCovered(typeGenDepth) shouldBe true @@ -33,7 +33,7 @@ class RTypeTests extends PropSpec with PropertyChecks with Matchers with RTypeGe property("RType generate value by type") { import scala.runtime.ScalaRunTime._ val minSuccess = MinSuccessful(PosInt.from(coverageThreshold).get) - forAll(extendedTypeGen(typeGenDepth), minSuccess) { t: RType[_] => + forAll(rtypeGen(typeGenDepth), minSuccess) { t: RType[_] => forAll(extendedValueGen(t)) { value => RTypeTestUtil.valueMatchesRType(value, t) shouldBe true } @@ -54,7 +54,7 @@ class RTypeTests extends PropSpec with PropertyChecks with Matchers with RTypeGe } val minSuccess = MinSuccessful(PosInt.from(coverageThreshold).get) - forAll(extendedTypeGen(typeGenDepth), minSuccess) { t => + forAll(rtypeGen(typeGenDepth), minSuccess) { t => forAll(extendedValueGen(t)) { value => checkCopy(value, t.asInstanceOf[RType[Any]]) } diff --git a/library/src/test/scala/special/collections/CollGens.scala b/library/src/test/scala/special/collections/CollGens.scala index 19bd19ad0..d77b067d8 100644 --- a/library/src/test/scala/special/collections/CollGens.scala +++ b/library/src/test/scala/special/collections/CollGens.scala @@ -4,129 +4,21 @@ import scala.collection.mutable.ArrayBuffer import org.scalacheck.util.Buildable import scala.collection.mutable -import org.scalacheck.{Arbitrary, Gen} +import org.scalacheck.Gen import scalan._ -import special.collection.{Coll, CollBuilder, CollOverArray, CollOverArrayBuilder, PairColl, ReplColl} +import special.collection.{Coll, ReplColl} -import scala.reflect.ClassTag -import scala.util.Random -trait CollGens { testSuite => +trait CollGens extends RTypeGens { testSuite => import Gen._ - val builder: CollBuilder = new CollOverArrayBuilder - val monoid = builder.Monoids.intPlusMonoid - val valGen = choose(-100, 100) - val indexGen = choose(0, 100) - val replacedGen = choose(0, 100) - val lenGen = choose(0, 100) - - val byteGen = choose[Byte](Byte.MinValue, Byte.MaxValue) - val shortGen = choose[Short](Short.MinValue, Short.MaxValue) - val intGen = choose[Int](Int.MinValue, Int.MaxValue) - val longGen = choose[Long](Long.MinValue, Long.MaxValue) - val charGen = choose[Char](Char.MinValue, Char.MaxValue) - val floatGen = choose[Float](Float.MinValue, Float.MaxValue) - val doubleGen = choose[Double](Double.MinValue, Double.MaxValue) - - def getArrayGen[T](valGen: Gen[T], count: Int = 100) - (implicit evb: Buildable[T,Array[T]], evt: Array[T] => Traversable[T]): Gen[Array[T]] = { - containerOfN[Array, T](count, valGen) - } - - def getCollOverArrayGen[T: RType](valGen: Gen[T], count: Int = 100): Gen[Coll[T]] = { - containerOfN[Array, T](count, valGen).map(builder.fromArray(_)) - } - - def getCollReplGen[T: RType](lenGen: Gen[Int], valGen: Gen[T], count: Int = 100): Gen[Coll[T]] = { - for { l <- lenGen; v <- valGen } yield builder.replicate(l, v) - } - - def getCollViewGen[A: RType](valGen: Gen[Coll[A]]): Gen[Coll[A]] = { - valGen.map(builder.makeView(_, identity[A])) - } - - def getCollViewGen[A, B: RType](valGen: Gen[Coll[A]], f: A => B): Gen[Coll[B]] = { - valGen.map(builder.makeView(_, f)) - } - - def getCollPairGenFinal[A: RType, B: RType](collGenLeft: Gen[Coll[A]], collGenRight: Gen[Coll[B]]): Gen[PairColl[A, B]] = { - for { left <- collGenLeft; right <- collGenRight } yield builder.pairColl(left, right) - } - - def getCollPairGenRight[A: RType, B: RType, C: RType](collGenLeft: Gen[Coll[A]], collGenRight: Gen[PairColl[B, C]]): Gen[PairColl[A, (B, C)]] = { - for { left <- collGenLeft; right <- collGenRight } yield builder.pairColl(left, right) - } - - def getCollPairGenLeft[A: RType, B: RType, C: RType](collGenLeft: Gen[PairColl[A, B]], collGenRight: Gen[Coll[C]]): Gen[PairColl[(A, B), C]] = { - for { left <- collGenLeft; right <- collGenRight } yield builder.pairColl(left, right) - } - - def getCollPairGenBoth[A: RType, B: RType, C: RType, D: RType](collGenLeft: Gen[PairColl[A, B]], collGenRight: Gen[PairColl[C, D]]): Gen[PairColl[(A, B), (C, D)]] = { - for { left <- collGenLeft; right <- collGenRight } yield builder.pairColl(left, right) - } - - // TODO: there's a need in generator that produces collections with different elements and the same type scheme - def getSuperGen[T: RType](length: Int = 1, collGen: Gen[Coll[T]]): Gen[PairColl[_, _]] = { - length match { - case 0 => { - Gen.oneOf(getCollPairGenFinal(collGen, collGen), - getCollPairGenFinal(collGen, collGen)) - } - case _ => { - getSuperGen(length - 1, collGen) match { - case lg: Gen[PairColl[RType[_], RType[_]]]@unchecked => { - getSuperGen(length - 1, collGen) match { - case rg: Gen[PairColl[RType[_], RType[_]]]@unchecked => { - val gen = Gen.oneOf( - getCollPairGenFinal(collGen, collGen), - getCollPairGenLeft(lg, collGen), - getCollPairGenRight(collGen, rg), - getCollPairGenBoth(lg, rg), - ) - return gen - } - case _ => throw new RuntimeException("Invalid rGen") - } - } - case _ => throw new RuntimeException("Invalid lGen") - } - } - } - } - - val bytesArrayGen: Gen[Array[Byte]] = getArrayGen[Byte](byteGen) //containerOfN[Array, Byte](100, byteGen) - val arrayGen: Gen[Array[Int]] = getArrayGen[Int](valGen) //containerOfN[Array, Int](100, valGen) - val indexesGen = containerOfN[Array, Int](10, indexGen).map(arr => builder.fromArray(arr.distinct.sorted)) - - val collOverArrayGen = getCollOverArrayGen(valGen) //arrayGen.map(builder.fromArray(_)) - - val bytesOverArrayGen = getCollOverArrayGen(byteGen) - val replCollGen = getCollReplGen(lenGen, valGen) - val replBytesCollGen = getCollReplGen(lenGen, byteGen) - - val lazyCollGen = getCollViewGen(collOverArrayGen) - val lazyByteGen = getCollViewGen(bytesOverArrayGen) - - def easyFunction(arg: Int): Int = arg * 20 + 300 - def inverseEasyFunction(arg: Int): Int = (arg - 300) / 20 - - val lazyFuncCollGen = getCollViewGen[Int, Int](collOverArrayGen, easyFunction) - val lazyUnFuncCollGen = getCollViewGen[Int, Int](lazyFuncCollGen, inverseEasyFunction) - - val collGen = Gen.oneOf(collOverArrayGen, replCollGen, lazyCollGen, lazyUnFuncCollGen) - val bytesGen = Gen.oneOf(bytesOverArrayGen, replBytesCollGen, lazyByteGen) - - val innerGen = Gen.oneOf(collOverArrayGen, replCollGen) - - val superGenInt = getSuperGen(1, Gen.oneOf(collOverArrayGen, replCollGen, lazyCollGen, lazyUnFuncCollGen)) - val superGenByte = getSuperGen(1, Gen.oneOf(bytesOverArrayGen, replBytesCollGen, lazyByteGen)) - val superGen = Gen.oneOf(superGenInt, superGenByte) + val indexGen = choose(0, 100) - val allGen = Gen.oneOf(superGen, collGen) + val monoid = builder.Monoids.intPlusMonoid - implicit val arbColl = Arbitrary(collGen) - implicit val arbBytes = Arbitrary(bytesGen) + def hashCodeLt0[T](x: T) = x.hashCode() < 0 + def hashCodeInc[T](x: T) = x.hashCode() + 1 + def plusHashcode[T](p: (T,T)) = plus(p._1.hashCode(), p._2.hashCode()) def eq0(x: Int) = x == 0 def lt0(x: Int) = x < 0 @@ -140,16 +32,6 @@ trait CollGens { testSuite => case _ => false } - def complexFunction(arg: Int): Int = { - var i = 0 - var res = 0 - while (i < 10) { - res += arg - i - i += 1 - } - res - } - implicit def buildableColl[T:RType] = new Buildable[T,Coll[T]] { def builder = new mutable.Builder[T,Coll[T]] { val al = new ArrayBuffer[T] diff --git a/library/src/test/scala/special/collections/CollsTests.scala b/library/src/test/scala/special/collections/CollsTests.scala index cd9b6f034..ffc3dbd1d 100644 --- a/library/src/test/scala/special/collections/CollsTests.scala +++ b/library/src/test/scala/special/collections/CollsTests.scala @@ -1,49 +1,109 @@ package special.collections -import special.collection.{ReplColl, PairOfCols, PairColl, CReplColl, Coll} -import org.scalacheck.{Shrink, Gen} -import org.scalatest.{PropSpec, Matchers} +import org.scalacheck.Gen +import org.scalatest.{Matchers, PropSpec} import org.scalatest.prop.PropertyChecks -import scalan.RType -import scalan.RType.PairType +import scalan.{GenConfiguration, RType, RTypeTestUtil, RTypeUtil} class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGens { testSuite => import Gen._ + import scalan.RType._ + import special.collection._ import special.collection.ExtensionMethods._ - property("Coll.indices") { - val minSuccess = MinSuccessful(30) - forAll(collGen, collGen, minSuccess) { (col1: Coll[Int], col2: Coll[Int]) => - col1.indices.toArray shouldBe col1.toArray.indices.toArray -// col1.zip(col2).length shouldBe math.min(col1.length, col2.length) -// TODO col1.zip(col2).indices.arr shouldBe col1.arr.zip(col2.arr).indices.toArray + val typeGenerationDepth = 5 + val testConfiguration = new GenConfiguration(maxArrayLength = 10) + def valueGen[T](t: RType[T]): Gen[T] = rtypeValueGen(testConfiguration)(t) + + val testMinSuccess = MinSuccessful(100) + val typeMinSuccess = MinSuccessful(5) + val successfulAtLeastOnce = MinSuccessful(1) + + val intCollRtype = CollType(IntType) + + /* Test example: + + property("Some prop") { + forAll(extendedCollTypeGen(typeGenerationDepth), testMinSuccess) { t: RType[Coll[_]] => + forAll(valueGen(t), valueGen(t), typeMinSuccess) { (col1: Coll[_], col2: Coll[_]) => + } + } + } + + */ + import scala.runtime.ScalaRunTime._ + + def arrayEq[T](first: Array[T], second: Array[T]) = { + (first.length == second.length && first.zip(second).forall(pair => pair._1 == pair._2)) shouldBe true + } + + def tItem(collType: RType[Coll[_]]): RType[_] = collType.asInstanceOf[RType[_]] match { + case ct: CollType[a] => + ct.tItem + case rct: ReplCollType[a] => rct.tItem + case _ => throw new RuntimeException("Not a collection") + } + + property("Coll.slice") { + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), indexGen, indexGen, typeMinSuccess) { (col, from, until) => + val res = col.slice(from, until) + res.toArray shouldBe col.toArray.slice(from, until) + } } - forAll(superGen, minSuccess) { cl => - cl.indices.toArray shouldBe cl.toArray.indices.toArray + } + + property("Coll.indices") { + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), valueGen(tCol), typeMinSuccess) { (col1: Coll[_], col2: Coll[_]) => + col1.indices.toArray shouldBe col1.toArray.indices.toArray + col1.zip(col2).length shouldBe math.min(col1.length, col2.length) + col1.zip(col2).indices.toArray shouldBe col1.toArray.zip(col2.toArray).indices.toArray + } } } property("Coll.flatMap") { - forAll(containerOfN[Coll, Int](3, valGen), collGen) { (zs, col) => - val matrix = zs.map(_ => col) - val res = zs.zip(matrix).flatMap(_._2) - res.toArray shouldBe zs.toArray.flatMap(_ => col.toArray) + def runTest[T](externalCol: Coll[Coll[T]], internalCol: Coll[T])(implicit tC: RType[Coll[T]]) = { + val matrix = externalCol.map(_ => internalCol)(tC) + val res = externalCol.zip(matrix) + val ret = res.flatMap(x => x._2)(tItem(tC.asInstanceOf[RType[Coll[_]]]).asInstanceOf[RType[T]]) + + val first = ret.toArray + val second = externalCol.toArray.flatMap(_ => internalCol.toArray).array.asInstanceOf[Array[T]] + arrayEq(first, second) + } + forAll(arrayTypeGen(collTypeGen(typeGenerationDepth - 1), 1).asInstanceOf[Gen[ArrayType[Coll[_]]]], testMinSuccess) { t => + forAll(valueGen(t.tA), valueGen(t), typeMinSuccess) { (col, zsArr) => + val arrayItemType = t.tA + val zs = builder.fromArray(zsArr)(arrayItemType) + runTest(zs.asInstanceOf[Coll[Coll[Any]]], col.asInstanceOf[Coll[Any]])(arrayItemType.asInstanceOf[RType[Coll[Any]]]) + } } } property("Coll.segmentLength") { - forAll(collGen, indexGen) { (col, from) => - col.segmentLength(lt0, from) shouldBe col.toArray.segmentLength(lt0, from) + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tItem(tCol)), valueGen(tCol), indexGen, typeMinSuccess) { (item, col, from) => + col.segmentLength(_.equals(item), from) shouldBe col.toArray.segmentLength(_.equals(item), from) + } } - val minSuccess = minSuccessful(30) - forAll(superGen, indexGen, minSuccess) { (col, from) => - col.segmentLength(collMatchRepl, from) shouldBe col.toArray.segmentLength(collMatchRepl, from) + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), indexGen, typeMinSuccess) { (col, from) => + col.segmentLength(collMatchRepl, from) shouldBe col.toArray.segmentLength(collMatchRepl, from) + } } } property("Coll.indexWhere") { - forAll(collGen, indexGen) { (col, from) => + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tItem(tCol)), valueGen(tCol), indexGen, typeMinSuccess) { (item, col, from) => + col.segmentLength(_.equals(item), from) shouldBe col.toArray.segmentLength(_.equals(item), from) + } + } + + forAll(valueGen(intCollRtype), indexGen, testMinSuccess) { (col: Coll[Int], from: Int) => col.indexWhere(eq0, from) shouldBe col.toArray.indexWhere(eq0, from) def p2(ab: (Int, Int)) = eq0(ab._1) && eq0(ab._2) col.zip(col).indexWhere(p2, from) shouldBe col.toArray.zip(col.toArray).indexWhere(p2, from) @@ -51,14 +111,25 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen } property("Coll.indexOf") { - forAll(collGen, indexGen, valGen) { (col, from, elem) => - col.indexOf(elem, from) shouldBe col.toArray.indexOf(elem, from) - col.zip(col).indexOf((elem, elem), from) shouldBe col.toArray.zip(col.toArray).indexOf((elem, elem), from) + def runTest[T](colItem: T, col: Coll[T], from: Int)(implicit t: RType[T]) = { + col.indexOf(colItem, from) shouldBe col.toArray.indexOf(colItem, from) + col.zip(col).indexOf((colItem, colItem), from) shouldBe col.toArray.zip(col.toArray).indexOf((colItem, colItem), from) + } + + forAll(collTypeGen(typeGenerationDepth - 1), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tItem(tCol)), valueGen(tCol), indexGen, typeMinSuccess) { (item, col, from) => + runTest(item, col.asInstanceOf[Coll[Any]], from)(tCol.asInstanceOf[RType[Any]]) + } } } property("Coll.lastIndexWhere") { - forAll(collGen, indexGen) { (col, end) => + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tItem(tCol)), valueGen(tCol), indexGen, typeMinSuccess) { (item, col, end) => + col.lastIndexWhere(_.equals(item), end) shouldBe col.lastIndexWhere(_.equals(item), end) + } + } + forAll(valueGen(intCollRtype), indexGen, testMinSuccess) { (col: Coll[Int], end: Int) => col.lastIndexWhere(eq0, end) shouldBe col.lastIndexWhere(eq0, end) def p2(ab: (Int, Int)) = eq0(ab._1) && eq0(ab._2) col.zip(col).lastIndexWhere(p2, end) shouldBe col.toArray.zip(col.toArray).lastIndexWhere(p2, end) @@ -66,7 +137,15 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen } property("Coll.partition") { - forAll(collGen) { col => + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), typeMinSuccess) { (col) => + val (lsC, rsC) = col.partition(equals) + val (ls, rs) = col.toArray.partition(equals) + lsC.toArray shouldBe ls + rsC.toArray shouldBe rs + } + } + forAll(valueGen(intCollRtype)) { col => val (lsC, rsC) = col.partition(lt0) val (ls, rs) = col.toArray.partition(lt0) lsC.toArray shouldBe ls @@ -75,51 +154,77 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen } property("Coll.patch") { - forAll(collGen, choose(-100, 100), collGen, replacedGen) { (col, from, patch, replaced) => - whenever(from < col.length ) { - val patchedC = col.patch(from, patch, replaced) - val patched = col.toArray.patch(from, patch.toArray, replaced) - patchedC.toArray shouldBe patched + def runTest[T](col: Coll[T], patch: Coll[T], from: Int, replaced: Int)(implicit t: RType[T]) = { + val patchedC = col.patch(from, patch, replaced) + val patched = col.toArray.patch(from, patch.toArray, replaced) + arrayEq(patchedC.toArray, patched.array.asInstanceOf[Array[T]]) + } + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), valueGen(tCol), choose(-100, 100), indexGen, typeMinSuccess) { (col, patch, from, replaced) => + whenever(from < col.length) { + runTest(col.asInstanceOf[Coll[Any]], patch.asInstanceOf[Coll[Any]], from, replaced)(tItem(tCol).asInstanceOf[RType[Any]]) + } } } } - property("Coll.updated") { - forAll(collGen, indexGen, valGen) { (col, index, elem) => - whenever(index < col.length ) { - val patchedC = col.updated(index, elem) - val patched = col.toArray.updated(index, elem) - patchedC.toArray shouldBe patched - } - an[IndexOutOfBoundsException] should be thrownBy { - col.updated(col.length, elem) + property("Coll.updated") { + def runTest[T](col: Coll[T], patch: T, index: Int)(implicit t: RType[T]) = { + if (0 <= index && index < col.length) { + val patchedC = col.updated(index, patch) + val patched = col.toArray.updated(index, patch) + arrayEq(patchedC.toArray, patched.array.asInstanceOf[Array[T]]) + } else { + an[IndexOutOfBoundsException] should be thrownBy { + col.updated(index, patch) + } + an[IndexOutOfBoundsException] should be thrownBy { + col.updated(-1, patch) + } } - an[IndexOutOfBoundsException] should be thrownBy { - col.updated(-1, elem) + } + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tItem(tCol)), valueGen(tCol), indexGen, typeMinSuccess) { (patch, testColl, index) => + runTest(testColl.asInstanceOf[Coll[Any]], patch.asInstanceOf[Any], index)(tItem(tCol).asInstanceOf[RType[Any]]) } } } property("Coll.updateMany") { - forAll(collGen, indexesGen) { (col, indexes) => - whenever(indexes.forall(_ < col.length)) { - val updatedC = col.updateMany(indexes, indexes) + def runTest[T](col: Coll[T], patch: Coll[T], indexes: Coll[Int])(implicit t: RType[T]) = { + if (indexes.forall(x => x < col.length && x > -1)) { + val updatedC = col.updateMany(indexes, patch) val updated = col.toArray.clone() - for (i <- indexes) - updated.update(i, i) + for ((i, value) <- indexes.zip(patch)) + updated.update(i, value) updatedC.toArray shouldBe updated + } else { + if (col.length > 0) { + an[IndexOutOfBoundsException] should be thrownBy { + col.updateMany(builder.fromItems(col.length), col.slice(0, 1)) + } + an[IndexOutOfBoundsException] should be thrownBy { + col.updateMany(builder.fromItems(-1), col.slice(0, 1)) + } + } } - an[IndexOutOfBoundsException] should be thrownBy { - col.updateMany(builder.fromItems(col.length), builder.fromItems(0)) - } - an[IndexOutOfBoundsException] should be thrownBy { - col.updateMany(builder.fromItems(-1), builder.fromItems(0)) + } + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), valueGen(tCol), valueGen(CollType(IntType)), typeMinSuccess) { (col, update, indexes) => + runTest(col.asInstanceOf[Coll[Any]], + update.slice(0, + if (indexes.length > update.length) update.length else indexes.length + ).asInstanceOf[Coll[Any]], indexes)(tItem(tCol).asInstanceOf[RType[Any]]) } } } property("Coll methods") { - forAll(collGen, indexGen) { (col, index) => + val intTunedConf = new GenConfiguration( + maxArrayLength = testConfiguration.maxArrayLength, + intBorders = (-100, 100) + ) + forAll(rtypeValueGen(intTunedConf)(intCollRtype), indexGen) { (col, index) => { val res = col.sum(monoid) res shouldBe col.toArray.sum @@ -157,144 +262,129 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen val op = (in: (Int,(Int,Int))) => in._1 + in._2._1 + in._2._2 pairs.foldLeft(0, op) shouldBe pairs.toArray.foldLeft(0)((b,a) => op((b,a))) } - whenever(index < col.length) { + if (index < col.length) { val res = col(index) res shouldBe col.toArray(index) val res2 = col.getOrElse(index, index) res2 shouldBe col.toArray(index) } - + col.getOrElse(col.length, index) shouldBe index col.getOrElse(-1, index) shouldBe index } - forAll(superGen, indexGen) { (col, index) => - whenever(index < col.length) { - val res = col(index) - res shouldBe col.toArray(index) - } - } } - property("Coll.slice") { - forAll(collGen, indexGen, indexGen) { (col, from, until) => - whenever(until < col.length) { - val res = col.slice(from, until) - res.toArray shouldBe col.toArray.slice(from, until) + property("Coll.apply") { + def runTest[T](col: Coll[T], index: Int)(implicit t: RType[T]) = { + if (index < col.length) { + val res = col(index) + res shouldBe col.toArray(index) + } else { + an[IndexOutOfBoundsException] should be thrownBy { + col(index) + } } } - - forAll(superGen, indexGen, indexGen) { (col, from, until) => - whenever(until < col.length) { - val res = col.slice(from, until) - res.toArray shouldBe col.toArray.slice(from, until) + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), indexGen, typeMinSuccess) { (col, index) => + runTest(col.asInstanceOf[Coll[Any]], index)(tItem(tCol).asInstanceOf[RType[Any]]) } } } property("Coll.append") { - forAll(collGen, collGen, valGen, MinSuccessful(100)) { (col1, col2, v) => - - { - val res = col1.append(col2) - res.toArray shouldBe (col1.toArray ++ col2.toArray) - val pairs1 = col1.zip(col1) - val pairs2 = col2.zip(col2) - val apairs = pairs1.append(pairs2) - apairs.toArray shouldBe (pairs1.toArray ++ pairs2.toArray) - } - - { - val repl1 = builder.replicate(col1.length, v) - val repl2 = builder.replicate(col2.length, v) - val arepl = repl1.append(repl2) - - arepl.toArray shouldBe (repl1.toArray ++ repl2.toArray) - - val pairs1 = repl1.zip(repl1) - val pairs2 = repl2.zip(repl2) - val apairs = pairs1.append(pairs2) - apairs.toArray shouldBe (pairs1.toArray ++ pairs2.toArray) - - if (repl1.nonEmpty && repl2.nonEmpty) { - assert(arepl.isInstanceOf[CReplColl[Int]]) - apairs match { - case ps: PairOfCols[_,_] => - assert(ps.ls.isInstanceOf[CReplColl[Int]]) - assert(ps.rs.isInstanceOf[CReplColl[Int]]) - case _ => - assert(false, "Invalid type") - } - } - + def runTest[T](col1: Coll[T], col2: Coll[T])(implicit t: RType[T]) = { + val res = col1.append(col2) + arrayEq(res.toArray, (col1.toArray ++ col2.toArray).array.asInstanceOf[Array[T]]) + val pairs1 = col1.zip(col1) + val pairs2 = col2.zip(col2) + val apairs = pairs1.append(pairs2) + arrayEq(apairs.toArray, (pairs1.toArray ++ pairs2.toArray)) + } + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), valueGen(tCol), typeMinSuccess) { (col1, col2) => + val collItemType = tItem(tCol) + runTest(col1.asInstanceOf[Coll[Any]], col2.asInstanceOf[Coll[Any]])(collItemType.asInstanceOf[RType[Any]]) } } } property("Coll.mapReduce") { import scalan.util.CollectionUtil.TraversableOps - def m(x: Int) = (math.abs(x) % 10, x) - forAll(collGen) { col => - val res = col.mapReduce(m, plusF) + def m[T](x: T) = (x.hashCode() % 10, x) + def projectionSnd[T](x: (T, T)): T = x._2 + def takeSnd[T](x1: T, x2: T): T = x2 + def runTest[T](col: Coll[T])(implicit t: RType[T]) = { + val res = col.mapReduce(m, projectionSnd[T])(IntType, t.asInstanceOf[RType[T]]) val (ks, vs) = builder.unzip(res) - vs.toArray.sum shouldBe col.toArray.sum ks.length <= 10 shouldBe true - res.toArray shouldBe col.toArray.toIterable.mapReduce(m)(plus).toArray + res.toArray shouldBe col.toArray.toIterable.mapReduce(m)(takeSnd[T]).toArray + } + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), typeMinSuccess) { (col) => + val collItemType = tItem(tCol) + runTest(col.asInstanceOf[Coll[Any]])(collItemType.asInstanceOf[RType[Any]]) + } } } property("Coll.groupBy") { - def key(x: Int) = math.abs(x) % 10 - forAll(collGen) { col => + def runTest[T](col: Coll[T])(implicit t: RType[T]) = { val res = col.groupBy(key) val (ks, vs) = builder.unzip(res) - vs.flatten.toArray.sum shouldBe col.toArray.sum + vs.flatten.map(key).toArray.sum shouldBe col.toArray.map(key).sum ks.length <= 10 shouldBe true - val pairs = col.map(x => (key(x), x)) + val pairs = col.map(x => (key(x), x))(pairRType(IntType, col.tItem)) val res2 = pairs.groupByKey - val (ks2, vs2) = builder.unzip(res) + val (ks2, vs2) = builder.unzip(res2) ks shouldBe ks2 vs shouldBe vs2 } + def key[T](x: T) = x.hashCode() % 10 + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), typeMinSuccess) { (col) => + val collItemType = tItem(tCol) + runTest(col.asInstanceOf[Coll[Any]])(collItemType.asInstanceOf[RType[Any]]) + } + } } property("Coll.reverse") { - val minSuccess = minSuccessful(50) - forAll(allGen, minSuccess) { col => - val res = col.reverse - res.toArray shouldBe col.toArray.reverse - val pairs = col.zip(col) - pairs.reverse.toArray shouldBe pairs.toArray.reverse -// TODO should work -// val c1 = col.asInstanceOf[Coll[Any]] -// val appended = c1.append(c1) -// appended.toArray shouldBe (c1.toArray ++ c1.toArray) + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), typeMinSuccess) { (col) => + val res = col.reverse + res.toArray shouldBe col.toArray.reverse + val pairs = col.zip(col) + pairs.reverse.toArray shouldBe pairs.toArray.reverse + + val c1 = col.asInstanceOf[Coll[Any]] + val appended = c1.append(c1) + appended.toArray shouldBe (c1.toArray ++ c1.toArray) + } } } property("Coll.take") { - val minSuccess = minSuccessful(50) - forAll(allGen, minSuccess) { col => - val n = col.length / 2 - val res = col.take(n) - res.toArray shouldBe col.toArray.take(n) - val pairs = col.zip(col) - pairs.take(n).toArray shouldBe pairs.toArray.take(n) + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), typeMinSuccess) { (col) => + val n = col.length / 2 + val res = col.take(n) + res.toArray shouldBe col.toArray.take(n) + val pairs = col.zip(col) + pairs.take(n).toArray shouldBe pairs.toArray.take(n) + } } } property("Coll.distinct") { - forAll(collGen) { col => - val res = col.distinct - res.toArray shouldBe col.toArray.distinct - val pairs = col.zip(col) - pairs.distinct.toArray shouldBe pairs.toArray.distinct - } - forAll(superGen) { col => + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), typeMinSuccess) { (col) => val res = col.distinct res.toArray shouldBe col.toArray.distinct val pairs = col.zip(col) pairs.distinct.toArray shouldBe pairs.toArray.distinct + } } } @@ -318,159 +408,74 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen zip3.hashCode() shouldBe zip4.hashCode() zip4.hashCode() shouldBe zip1.hashCode() } - val minSuccess = minSuccessful(50) - forAll(byteGen, indexGen, minSuccess) { (x, n) => - val repl = builder.replicate(n, x) - val coll = builder.fromArray(Array.fill(n)(x)) - - checkColls(repl, coll) - } - forAll(shortGen, indexGen, minSuccess) { (x, n) => - val repl = builder.replicate(n, x) - val coll = builder.fromArray(Array.fill(n)(x)) - - checkColls(repl, coll) - } - forAll(intGen, indexGen, minSuccess) { (x, n) => - val repl = builder.replicate(n, x) - val coll = builder.fromArray(Array.fill(n)(x)) - - checkColls(repl, coll) - } - forAll(longGen, indexGen, minSuccess) { (x, n) => - val repl = builder.replicate(n, x) - val coll = builder.fromArray(Array.fill(n)(x)) - - checkColls(repl, coll) - } - forAll(charGen, indexGen, minSuccess) { (x, n) => - val repl = builder.replicate(n, x) - val coll = builder.fromArray(Array.fill(n)(x)) - - checkColls(repl, coll) - } - forAll(floatGen, indexGen, minSuccess) { (x, n) => - val repl = builder.replicate(n, x) - val coll = builder.fromArray(Array.fill(n)(x)) - - checkColls(repl, coll) - } - forAll (doubleGen, indexGen, minSuccess) { (x, n) => - val repl = builder.replicate(n, x) - val coll = builder.fromArray(Array.fill(n)(x)) - - checkColls(repl, coll) - } - forAll (indexGen, minSuccess) { (n) => - val replTrue = builder.replicate(n, true) - val collTrue = builder.fromArray(Array.fill(n)(true)) - val replFalse = builder.replicate(n, false) - val collFalse = builder.fromArray(Array.fill(n)(false)) - - checkColls(replTrue, collTrue) - checkColls(replFalse, collFalse) - } - forAll(indexGen, minSuccess) { n => - val repl = builder.replicate(n, builder.fromItems(Array(1, 2, 3))) - val coll = builder.fromArray(Array.fill(n)(builder.fromItems(Array(1, 2, 3)))) - - checkColls(repl, coll) - } - forAll(indexGen, indexGen, minSuccess) { (n, m) => - val repl = builder.replicate(n, builder.replicate(m, 1)) - val coll = builder.fromArray(Array.fill(n)(builder.fromArray(Array.fill(m)(1)))) - - checkColls(repl, coll) - } - // This tuple tests fail with previous implementation - forAll (byteGen, doubleGen, intGen, indexGen, minSuccess) { (b, d, i, n) => - val repl = builder.replicate(n, (b, i)) - val coll = builder.fromArray(Array.fill[(Byte, Int)](n)((b, i))) - - checkColls(repl, coll) - } - forAll (byteGen, doubleGen, intGen, indexGen, minSuccess) { (b, d, i, n) => - val repl = builder.replicate(n, (b, (i, (d, b)))) - val coll = builder.fromArray(Array.fill[(Byte, (Int, (Double, Byte)))](n)((b, (i, (d, b))))) - - checkColls(repl, coll) - } - forAll (byteGen, doubleGen, intGen, indexGen, indexGen, minSuccess) { (b, d, i, n, m) => - val repl = builder.replicate(n, (b, ((i, (("string", builder.replicate(m, n)), Array(1, 2, 3, 4))), (d, b)))) - val coll = builder.fromArray(Array.fill(n)((b, ((i, (("string", builder.fromArray(Array.fill(m)(n))), Array(1, 2, 3, 4))), (d, b))))) - - checkColls(repl, coll) + forAll(rtypeGen(typeGenerationDepth), testMinSuccess) { t: RType[_] => + forAll(valueGen(t), indexGen, typeMinSuccess) { (item, n) => + val repl = new CReplColl(item.asInstanceOf[Any], n)(t.asInstanceOf[RType[Any]]) + val coll = new CollOverArray(Array.fill(n)(item.asInstanceOf[Any]))(t.asInstanceOf[RType[Any]]) + checkColls(repl, coll) + } } + // TODO: Test with builder } - property("PairColl.mapFirst") { - val minSuccess = minSuccessful(30) - - forAll(collGen, minSuccess) { col => - val pairs = col.zip(col) - pairs.mapFirst(inc).toArray shouldBe pairs.toArray.map { case (x, y) => (inc(x), y) } - pairs.mapSecond(inc).toArray shouldBe pairs.toArray.map { case (x, y) => (x, inc(y)) } + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), typeMinSuccess) { col => + val pairs = col.zip(col) + pairs.mapFirst(hashCodeInc).toArray shouldBe pairs.toArray.map { case (x, y) => (hashCodeInc(x), y) } + pairs.mapSecond(hashCodeInc).toArray shouldBe pairs.toArray.map { case (x, y) => (x, hashCodeInc(y)) } + } } } property("Coll.unionSet") { - forAll(collGen, collGen) { (col1, col2) => + def runTest[T](col1: Coll[T], col2: Coll[T])(implicit t: RType[T]) = { val res = col1.unionSet(col2) - res.toArray shouldBe (col1.toArray.union(col2.toArray).distinct) - } - builder.replicate(2, 10).unionSet(builder.replicate(3, 10)).toArray shouldBe Array(10) - forAll(superGen) { - case cl1: Coll[(_, _)] => { - val res = cl1.unionSet(cl1) - res.toArray shouldBe (cl1.toArray.union(cl1.toArray).distinct) - } - case _ => assert(false, "Generator returned invalid PairColl") - } - /* TODO: simplify the above code - * match-case removal gives the following compilation error: - type mismatch; - found : special.collection.PairColl[_$1(in value res),_$2(in value res)] where type _$2(in value res), type _$1(in value res) - required: special.collection.Coll[(_$1(in method getSuperGen), _$2(in method getSuperGen))] - val res = col1.unionSet(col1) - */ + arrayEq(res.toArray, (col1.toArray.union(col2.toArray).distinct).array.asInstanceOf[Array[T]]) + } + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), valueGen(tCol), typeMinSuccess) { (col1, col2) => + runTest(col1.asInstanceOf[Coll[Any]], col2.asInstanceOf[Coll[Any]])(tItem(tCol).asInstanceOf[RType[Any]]) + } + } } property("Coll.diff") { - forAll(collGen, collGen) { (col1, col2) => + def runTest[T](col1: Coll[T], col2: Coll[T])(implicit t: RType[T]) = { val res = col1.diff(col2) res.toArray shouldBe (col1.toArray.diff(col2.toArray)) } - forAll(superGen) { - case col: Coll[(_, _)] => - val res = col.diff(col) - res.toArray shouldBe (col.toArray.diff(col.toArray)) - case _ => assert(false, "Generator returned invalid PairColl") // TODO make similar gens - } - /* TODO: simplify the above code - * match-case removal gives the following compilation error: - type mismatch; - found : special.collection.PairColl[_$1(in value res),_$2(in value res)] where type _$2(in value res), type _$1(in value res) - required: special.collection.Coll[(_$1(in method getSuperGen), _$2(in method getSuperGen))] - val res = col.diff(col) - */ - builder.replicate(2, 10).diff(builder.replicate(1, 10)).toArray shouldBe Array(10) + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), valueGen(tCol), typeMinSuccess) { (col1, col2) => + runTest(col1.asInstanceOf[Coll[Any]], col2.asInstanceOf[Coll[Any]])(tItem(tCol).asInstanceOf[RType[Any]]) + } + } } property("Coll.intersect") { - forAll(collGen, collGen) { (col1, col2) => + def runTest[T](col1: Coll[T], col2: Coll[T])(implicit t: RType[T]) = { val res = col1.intersect(col2) res.toArray shouldBe (col1.toArray.intersect(col2.toArray)) } - builder.replicate(2, 10).intersect(builder.replicate(3, 10)).toArray shouldBe Array(10, 10) + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), valueGen(tCol), typeMinSuccess) { (col1, col2) => + runTest(col1.asInstanceOf[Coll[Any]], col2.asInstanceOf[Coll[Any]])(tItem(tCol).asInstanceOf[RType[Any]]) + } + } } property("CollBuilder.xor") { - forAll(bytesGen, bytesGen) { (col1, col2) => - val n = col1.length min col2.length - val c1 = col1.take(n) - val c2 = col2.take(n) - builder.xor(c1, c2).toArray shouldBe c1.toArray.zip(c2.toArray).map { case (l,r) => (l ^ r).toByte } + def runTest[T](col1: Coll[T], col2: Coll[T])(implicit t1: RType[T]) = { + val n = if (col1.length < col2.length) col1.length else col2.length + val c1 = col1.take(n).asInstanceOf[Coll[Byte]] + val c2 = col2.take(n).asInstanceOf[Coll[Byte]] + builder.xor(c1, c2).toArray shouldBe c1.toArray.zip(c2.toArray).map { case (l, r) => (l ^ r).toByte } + } + forAll(valueGen(CollType(ByteType)), valueGen(ReplCollType(ByteType)), typeMinSuccess) { (col1, col2) => + runTest(col1, col1)(ByteType) + runTest(col1, col2)(ByteType) + runTest(col2, col1)(ByteType) + runTest(col2, col2)(ByteType) } } @@ -492,19 +497,22 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen val (ks, vs) = builder.unzip(res) vs.sum(monoid) shouldBe (col.sum(monoid) * 2 + col.map(_ + 5).sum(monoid)) } -// test(builder.fromItems(0)) -// val gen = containerOfN[Array, Int](100, choose(20, 100)) -// .map(xs => builder.fromArray(xs.distinct)) - forAll(collGen) { col => + val intTunedConf = new GenConfiguration( + maxArrayLength = testConfiguration.maxArrayLength, + intBorders = (-100, 100) + ) + forAll(rtypeValueGen(intTunedConf)(intCollRtype)) { (col) => test(col) } } property("CViewColl.correctWork") { - forAll(collGen) { coll => - val view = builder.makeView(coll, complexFunction) - val usual = coll.map(complexFunction) - view.toArray shouldBe usual.toArray + forAll(collTypeGen(typeGenerationDepth), testMinSuccess) { tCol: RType[Coll[_]] => + forAll(valueGen(tCol), typeMinSuccess) { (col) => + val view = builder.makeView(col, hashCodeInc) + val usual = col.map(hashCodeInc) + view.toArray shouldBe usual.toArray + } } } @@ -520,7 +528,6 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen val tokensArr = ids.zip(arr1) case class NoShrink[T](x: T) - val collGen = Gen.oneOf(builder.fromArray(arr1), builder.fromArray(arr2), builder.fromItems(1, 2, 3)).map(NoShrink(_)) val replGen = Gen.oneOf(builder.fromArray(repl1), builder.replicate(3, 1)).map(NoShrink(_)) val idsGen = Gen.oneOf(builder.fromArray(ids), builder.fromArray(ids)).map(NoShrink(_))