From af89bbb4ba7280860840f8d7d5b8ee6e45cf61ba Mon Sep 17 00:00:00 2001 From: Rex Kerr Date: Mon, 15 Apr 2019 17:41:03 -0700 Subject: [PATCH] Latest 2.13.x with Stepper and Accumulator tests. --- laws/src/main/scala/Flag.scala | 17 ++-- laws/src/main/scala/Generator.scala | 27 +++++- laws/src/main/scala/Instantiator.scala | 105 +++++++++++++++------ laws/src/main/scala/Laws.scala | 126 ++++++++++++++++++++----- laws/src/main/scala/Test.scala | 6 +- 5 files changed, 221 insertions(+), 60 deletions(-) diff --git a/laws/src/main/scala/Flag.scala b/laws/src/main/scala/Flag.scala index 128ca1e..162f8f2 100644 --- a/laws/src/main/scala/Flag.scala +++ b/laws/src/main/scala/Flag.scala @@ -25,29 +25,34 @@ object Flag { val SEQ = F // Is a sequence val SET = F // Is a set val STR = F // Uses strings as the element type + val VIEW = F // Is a view (normally doesn't matter, except views are lazy) // Unusual "collections" that are not expected to behave exactly like others val ARRAY = F // Is an Array val STRING = F // Is a String - val STRAW = F // strawman collections (when used together with regular collections) val ORDERLY = F // Collection is sorted, but can't maintain itself with all operations as it might lose its ordering val ONCE = F // Collection is consumed on traversal val INDEF = F // Collection's size is not yet fixed (lazy collections) val SPECTYPE= F // Collection has constraints on element type, which makes some operations not work val BITSET = F // Collection is specificially a bitset (mutable or immutable) val INSORD = F // Collection traverses itself in insertion order even though it's not intrinsically ordered - + val SPLITS = F // Collection can produce an efficiently splitting stepper + val STAGGER = F // Collection has an efficient stepper but traverses in unusual order + // Everything down here is _highly_ dubious behavior but is included to get tests to pass val PRIORITYQUEUE_IS_SPECIAL = F // Inconsistent behavior regarding what is dequeued (ordered) vs. not + val ACC_SPEC = F // Specialized accumulators revert to more general type in more cases than some other collections // Workarounds for identified bugs go here. val BITSET_MAP_AMBIG = F // Bit maps don't know whether to use StrictOptimized or SortedSet ops for map. - val BITSET_ZIP_AMBIG = F // Same problem with zipping + val BITSET_ZIP_AMBIG = F // Same problem with zipping + val INEFFICIENT_BUG = F // Some things should split efficiently but don't // Pure bugs that aren't fixed yet - val LISTBUF_PIP_11438 = F // ListBuffer throws an exception on a no-op patchInPlace - val QUEUE_SLIDE_11440 = F // Queue and ArrayStack will not give you an underfull sliding window (everything else does) - val PQ_MIP_NPE_11439 = F // Priority Queue can just give null when empty! + val LISTBUF_PIP_11438 = X // ListBuffer throws an exception on a no-op patchInPlace + val QUEUE_SLIDE_11440 = X // Queue and ArrayStack will not give you an underfull sliding window (everything else does) + val PQ_MIP_NPE_11439 = X // Priority Queue can just give null when empty! + val CPMH_TYPE_11449 = X // CollisionProofHashMap loses its type even when it needn't // Mysterious bugs that can't easily be replciated val SORTWITH_INT_CCE = F // Array (but nothing else) gives class cast error in `sortWith` on ints! Can't reproduce in REPL. diff --git a/laws/src/main/scala/Generator.scala b/laws/src/main/scala/Generator.scala index 0180cfe..67db702 100644 --- a/laws/src/main/scala/Generator.scala +++ b/laws/src/main/scala/Generator.scala @@ -234,6 +234,13 @@ object AllIntGenerators { val wrappedArray = register(io.Mut)(_.wrappedArray()) } + /** Generator for accumulators and anything else used in conversions */ + object Conv { + // TODO--get Ops-like abstractions to work; then this can be used + // val accumulator = register(io.Conv)(_.accumulator()) + val anyAccumulator = register(io.Conv)(_.anyAccumulator()) + } + /** Generator for iterators and views. */ object Root { val iterator = register(io.Root)(_.iterator()) @@ -255,8 +262,13 @@ object AllIntGenerators { val bitSet = register(io.MutInt)(_.bitSet(), "collection.mutable.BitSet") } + /** Generator for Int-specialized accumulators */ + object ConvInt { + val intAccumulator = register(io.ConvInt)(_.intAccumulator(), "scala.jdk.IntAccumulator") + } + /** This line is needed to actually perform the registration of all generators! */ - val force = Imm :: Mut :: Root :: ImmInt :: MutInt :: Nil + val force = Imm :: Mut :: Conv :: Root :: ImmInt :: MutInt :: ConvInt :: Nil /** All registered generators */ lazy val all = everyoneBuffer.result @@ -347,6 +359,13 @@ object AllStrGenerators { val wrappedArray = register(io.Mut)(_.wrappedArray()) } + /** Generator for accumulators and anything else used in conversions */ + object Conv { + // TODO--get Ops-like abstractions to work; then this can be used + // val accumulator = register(io.Conv)(_.accumulator()) + val anyAccumulator = register(io.Conv)(_.anyAccumulator()) + } + /** Generator for iterators. */ object Root { val iterator = register(io.Root)(_.iterator()) @@ -358,7 +377,7 @@ object AllStrGenerators { } /** This line is needed to actually perform the registration of all generators! */ - val force = Imm :: Mut :: Root :: Nil + val force = Imm :: Mut :: Conv :: Root :: Nil /** All registered generators */ lazy val all = everyoneBuffer.result @@ -406,6 +425,8 @@ object AllLongStrGenerators { } object MutKV { + val collisionProofHashMap = + register(io.MutKV)(_.collisionProofHashMap()) val hashMap = register(io.MutKV)(_.hashMap()) val listMap = register(io.MutKV)(_.listMap()) val linkedHashMap = register(io.MutKV)(_.linkedHashMap()) @@ -463,6 +484,8 @@ object AllStrLongGenerators { } object MutKV { + val collisionProofHashMap = + register(io.MutKV)(_.collisionProofHashMap()) val hashMap = register(io.MutKV)(_.hashMap()) val listMap = register(io.MutKV)(_.listMap()) val linkedHashMap = register(io.MutKV)(_.linkedHashMap()) diff --git a/laws/src/main/scala/Instantiator.scala b/laws/src/main/scala/Instantiator.scala index 485b387..ff81a98 100644 --- a/laws/src/main/scala/Instantiator.scala +++ b/laws/src/main/scala/Instantiator.scala @@ -81,9 +81,9 @@ extends Exploratory[(A, Array[A], Array[A])] { } // MUST use lower-camel-cased collection class name for code generator to work properly! - val arraySeq = C(collection.immutable.ArraySeq unsafeWrapArray _, SEQ) - val hashSet = C(_.to(collection.immutable.HashSet), SET) - val indexedSeq = C(_.to(collection.immutable.IndexedSeq), SEQ) + val arraySeq = C(collection.immutable.ArraySeq unsafeWrapArray _, SEQ, SPLITS) + val hashSet = C(_.to(collection.immutable.HashSet), SET, SPLITS) + val indexedSeq = C(_.to(collection.immutable.IndexedSeq), SEQ, SPLITS) val iterable = C(_.to(collection.immutable.Iterable)) val lazyList = C( a => collection.immutable.LazyList.from(0).takeWhile(_ < a.length).map(i => a(i)), @@ -100,8 +100,8 @@ extends Exploratory[(A, Array[A], Array[A])] { SEQ, INDEF ) val traversable = C(_.to(collection.immutable.Traversable)) - val treeSet = C(_.to(collection.immutable.TreeSet), SET, ORDERLY) - val vector = C(_.toVector, SEQ) + val treeSet = C(_.to(collection.immutable.TreeSet), SET, ORDERLY, SPLITS) + val vector = C(_.toVector, SEQ, SPLITS) } object Mut extends Instance.PackagePath { @@ -121,24 +121,46 @@ extends Exploratory[(A, Array[A], Array[A])] { } // MUST use lower-camel-cased collection class name for code generator to work properly! - val array = C(_.clone, SEQ, ARRAY, SORTWITH_INT_CCE).moreMethods(MethodChecker.from[collection.ArrayOps[A]]) - val arrayBuffer = C(_.to(collection.mutable.ArrayBuffer), SEQ) - val arrayDeque = C(_.to(collection.mutable.ArrayDeque), SEQ, QUEUE_SLIDE_11440) - val arraySeq = C(_.to(collection.mutable.ArraySeq), SEQ) - val arrayStack = C(_.to(collection.mutable.ArrayStack), SEQ, QUEUE_SLIDE_11440) + val array = C(_.clone, SEQ, ARRAY, SORTWITH_INT_CCE, SPLITS).moreMethods(MethodChecker.from[collection.ArrayOps[A]]) + val arrayBuffer = C(_.to(collection.mutable.ArrayBuffer), SEQ, SPLITS) + val arrayDeque = C(_.to(collection.mutable.ArrayDeque), SEQ, SPLITS, QUEUE_SLIDE_11440) + val arraySeq = C(_.to(collection.mutable.ArraySeq), SEQ, SPLITS) + val arrayStack = C(_.to(collection.mutable.ArrayStack), SEQ, SPLITS, QUEUE_SLIDE_11440) val buffer = C(_.to(collection.mutable.Buffer), SEQ) - val hashSet = C(_.to(collection.mutable.HashSet), SET) - val indexedSeq = C(_.to(collection.mutable.IndexedSeq), SEQ) + val hashSet = C(_.to(collection.mutable.HashSet), SET, SPLITS) + val indexedSeq = C(_.to(collection.mutable.IndexedSeq), SEQ, SPLITS) val iterable = C(_.to(collection.mutable.Iterable)) - val linkedHashSet= C(_.to(collection.mutable.LinkedHashSet), SET) + val linkedHashSet= C(_.to(collection.mutable.LinkedHashSet), SET, SPLITS, STAGGER) val listBuffer = C(_.to(collection.mutable.ListBuffer), SEQ, LISTBUF_PIP_11438) val priorityQueue= C(_.to(collection.mutable.PriorityQueue), ORDERLY, PRIORITYQUEUE_IS_SPECIAL, PQ_MIP_NPE_11439) - val queue = C(_.to(collection.mutable.Queue), SEQ, QUEUE_SLIDE_11440) + val queue = C(_.to(collection.mutable.Queue), SEQ, SPLITS, QUEUE_SLIDE_11440) val seq = C(_.to(collection.mutable.Seq), SEQ) - val stack = C(_.to(collection.mutable.Stack), QUEUE_SLIDE_11440) - val treeSet = C(_.to(collection.mutable.TreeSet), SET, ORDERLY) + val stack = C(_.to(collection.mutable.Stack), SPLITS, QUEUE_SLIDE_11440) + val treeSet = C(_.to(collection.mutable.TreeSet), SET, SPLITS, ORDERLY) // val unrolledBuffer = C(_.to(collection.mutable.UnrolledBuffer), SEQ) // Unrolled buffer is weird! - val wrappedArray = C(_.clone: collection.mutable.WrappedArray[A], SEQ) + val wrappedArray = C(_.clone: collection.mutable.WrappedArray[A], SEQ, SPLITS) + } + + object Conv extends Instance.PackagePath { + def nickname = "Conv" + def fullyQualified = "scala.jdk" + def C[CC: TypeTag: Sizable](ccf: Array[A] => CC, flags: Flag*)(implicit nm: sourcecode.Name): Deployed[A, CC] = { + val gen = inst.makeWith(ccf, flags: _*)(nm, implicitly[TypeTag[CC]], implicitly[Sizable[CC]]) + val ans = new Deployed[A, CC] { + val secretly = gen + var accesses: Int = 0 + val name = nm.value.toString + def group = typeTagA.tpe.toString + "in " + nickname + def apply(): Instance.FromArray[A, CC] = { accesses += 1; secretly } + } + registry += ans + ans + } + + // TODO--use the generic builder in a productive way; hard because it's Ops-like not its own collection + // val accumulator = C(a => collection.convert.Accumulator from a, SEQ) + + val anyAccumulator = C(a => scala.jdk.AnyAccumulator(a.toSeq: _*), SEQ, SPLITS) } object Root extends Instance.PackagePath { @@ -172,7 +194,7 @@ extends Exploratory[(A, Array[A], Array[A])] { // MUST use lower-camel-cased collection class name for code generator to work properly! val iterator = C(a => (new IteratorKnowsSize[A](a)): Iterator[A], ONCE, INDEF) - val view = C(a => a.to(collection.immutable.Vector).view: scala.collection.View[A]) + val view = C(a => a.to(collection.immutable.Vector).view: scala.collection.View[A], VIEW) // These don't work because they take arguments of a different type than they are themselves // val indexedSeqView = C(a => a.view: scala.collection.IndexedSeqView[A]) @@ -228,12 +250,12 @@ trait InstantiatorsOfKV[K, V] extends Exploratory[((K, V), Array[(K, V)], Array[ } // MUST use lower-camel-cased collection class name for code generator to work properly! - val hashMap = C({ a => val mb = collection.immutable.HashMap.newBuilder[K, V]; for (kv <- a) mb += kv; mb.result }) + val hashMap = C({ a => val mb = collection.immutable.HashMap.newBuilder[K, V]; for (kv <- a) mb += kv; mb.result }, SPLITS) val listMap = C({ a => val mb = collection.immutable.ListMap.newBuilder[K, V]; for (kv <- a) mb += kv; mb.result }) val sortedMap = C({ a => val mb = collection.immutable.SortedMap.newBuilder[K, V]; for (kv <- a) mb += kv; mb.result }) - val treeMap = C({ a => val mb = collection.immutable.TreeMap.newBuilder[K, V]; for (kv <- a) mb += kv; mb.result }) + val treeMap = C({ a => val mb = collection.immutable.TreeMap.newBuilder[K, V]; for (kv <- a) mb += kv; mb.result }, SPLITS) val treeSeqMap = C({ a => val mb = collection.immutable.TreeSeqMap.newBuilder[K, V]; for (kv <- a) mb += kv; mb.result }, INSORD) - val vectorMap = C({ a => val mb = collection.immutable.VectorMap.newBuilder[K, V]; for (kv <- a) mb += kv; mb.result }, INSORD) + val vectorMap = C({ a => val mb = collection.immutable.VectorMap.newBuilder[K, V]; for (kv <- a) mb += kv; mb.result }, INSORD, SPLITS, INEFFICIENT_BUG) } object MutKV extends Instance.PackagePath { @@ -253,12 +275,15 @@ trait InstantiatorsOfKV[K, V] extends Exploratory[((K, V), Array[(K, V)], Array[ } // MUST use lower-camel-cased collection class name for code generator to work properly! - val hashMap = C({ a => val m = new collection.mutable.HashMap[K, V]; for (kv <- a) m += kv; m }) + import collection.mutable.CollisionProofHashMap + val collisionProofHashMap = + C({ a => val m = new CollisionProofHashMap[K, V]; for (kv <- a) m += kv; m }, CPMH_TYPE_11449) + val hashMap = C({ a => val m = new collection.mutable.HashMap[K, V]; for (kv <- a) m += kv; m }, SPLITS) val listMap = C({ a => val m = new collection.mutable.ListMap[K, V]; for (kv <- a) m += kv; m }) - val linkedHashMap = C({ a => val m = new collection.mutable.LinkedHashMap[K, V]; for (kv <- a) m += kv; m }, INSORD) + val linkedHashMap = C({ a => val m = new collection.mutable.LinkedHashMap[K, V]; for (kv <- a) m += kv; m }, INSORD, STAGGER) val openHashMap = C({ a => val m = new collection.mutable.OpenHashMap[K, V]; for (kv <- a) m += kv; m }) val sortedMap = C({ a => val m = collection.mutable.SortedMap.empty[K, V]; for (kv <- a) m += kv; m }) - val treeMap = C({ a => val m = new collection.mutable.TreeMap[K, V]; for (kv <- a) m += kv; m }) + val treeMap = C({ a => val m = new collection.mutable.TreeMap[K, V]; for (kv <- a) m += kv; m }, SPLITS) val weakHashMap = C({ a => val m = new collection.mutable.WeakHashMap[K, V]; for (kv <- a) m += kv; m }) } } @@ -298,9 +323,10 @@ object InstantiatorsOfInt extends InstantiatorsOf[Int] { protected implicit def classTagA = ClassTagSource.classTagInt protected def allFlags = Array(INT) - protected implicit val sizeOfRange = new Sizable[collection.immutable.Range] { def sizeof(r: collection.immutable.Range) = r.size } + protected implicit val sizeOfRange = new Sizable[collection.immutable.Range] { def sizeof(r: collection.immutable.Range) = r.size } protected implicit val sizeOfIBitSet = new Sizable[collection.immutable.BitSet] { def sizeof(s: collection.immutable.BitSet) = s.size } - protected implicit val sizeOfMBitSet = new Sizable[collection.mutable.BitSet] { def sizeof(s: collection.mutable.BitSet) = s.size } + protected implicit val sizeOfMBitSet = new Sizable[collection.mutable.BitSet] { def sizeof(s: collection.mutable.BitSet) = s.size } + protected implicit val sizeOfIAccum = new Sizable[scala.jdk.IntAccumulator] { def sizeof(a: scala.jdk.IntAccumulator) = a.size } /** Extra instantiators specific to Ints in immutable collections */ object ImmInt extends Instance.PackagePath { @@ -323,7 +349,7 @@ object InstantiatorsOfInt extends InstantiatorsOf[Int] { // MUST use lower-camel-cased collection clasTs name for code generator to work properly! val bitSet = C( { a => val b = collection.immutable.BitSet.newBuilder; a.foreach{ x => if (x >= 0) b += x }; b.result }, - SET, ORDERLY, SPECTYPE, BITSET, BITSET_MAP_AMBIG, BITSET_ZIP_AMBIG + SET, ORDERLY, SPECTYPE, BITSET, SPLITS, BITSET_MAP_AMBIG, BITSET_ZIP_AMBIG ) //val range = C({ a => if (a.length % 3 == 0) 0 until a.length else 0 to a.length }) } @@ -349,10 +375,29 @@ object InstantiatorsOfInt extends InstantiatorsOf[Int] { // MUST use lower-camel-cased collection class name for code generator to work properly! val bitSet = C( { a => val b = new collection.mutable.BitSet; a.foreach{ x => if (x >= 0) b += x }; b }, - SET, ORDERLY, SPECTYPE, BITSET, BITSET_MAP_AMBIG, BITSET_ZIP_AMBIG + SET, ORDERLY, SPECTYPE, BITSET, SPLITS, BITSET_MAP_AMBIG, BITSET_ZIP_AMBIG ) } + object ConvInt extends Instance.PackagePath { + def nickname = "ConvInt" + def fullyQualified = "scala.jdk" + def C[CC: TypeTag: Sizable](ccf: Array[Int] => CC, flags: Flag*)(implicit nm: sourcecode.Name): Deployed[Int, CC] = { + val gen = inst.makeWith(ccf, flags: _*)(nm, implicitly[TypeTag[CC]], implicitly[Sizable[CC]]) + val ans = new Deployed[Int, CC] { + val secretly = gen + var accesses: Int = 0 + val name = nm.value.toString + def group = typeTagA.tpe.toString + "in " + nickname + def apply(): Instance.FromArray[Int, CC] = { accesses += 1; secretly } + } + registry += ans + ans + } + + val intAccumulator = C(a => scala.jdk.IntAccumulator(a.toSeq: _*), SEQ, SPECTYPE, SPLITS, ACC_SPEC) + } + /** Singleton `Int` values to test */ lazy val possible_a = Array(0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, 23, 31, 47, 152, 3133, 1294814, -1, -2, -6, -19, -1915, -19298157) @@ -454,7 +499,7 @@ object InstantiatorsOfLongStr extends InstantiatorsOf[(Long, String)] with Insta registry += ans ans } - val longMap = C({ a => val m = new collection.mutable.LongMap[String]; for (kv <- a) m += kv; m }, SPECTYPE) + val longMap = C({ a => val m = new collection.mutable.LongMap[String]; for (kv <- a) m += kv; m }, SPECTYPE, SPLITS, INEFFICIENT_BUG) } /** Very limited set of possible singletons */ @@ -514,7 +559,7 @@ object InstantiatorsOfStrLong extends InstantiatorsOf[(String, Long)] with Insta registry += ans ans } - val anyRefMap = C({ a => val m = new collection.mutable.AnyRefMap[String, Long]; for (kv <- a) m += kv; m }, SPECTYPE) + val anyRefMap = C({ a => val m = new collection.mutable.AnyRefMap[String, Long]; for (kv <- a) m += kv; m }, SPECTYPE, SPLITS, INEFFICIENT_BUG) } lazy val possible_a = Array("wish" -> 3L) diff --git a/laws/src/main/scala/Laws.scala b/laws/src/main/scala/Laws.scala index 21f0bcc..d38483d 100644 --- a/laws/src/main/scala/Laws.scala +++ b/laws/src/main/scala/Laws.scala @@ -183,7 +183,7 @@ set - only holds for collections that remove duplicates "x.`map`(bitset_f) sameAs { val y = collection.mutable.HashSet.empty[A]; x.foreach(y += _); y.map(bitset_f) }".law(BITSET) -"x sameType x.`map`(f)".law(ORDERLY.!) +"x sameType x.`map`(f)".law(ORDERLY.!, SPECTYPE.!, CPMH_TYPE_11449.!) """{ val flat = x.`flatMap`(xi => y.toList.take(intFrom(xi))) @@ -199,7 +199,7 @@ set - only holds for collections that remove duplicates flat sameAs ref }""".law(SET) -"x sameType x.`flatMap`(xi => y.toList.take(intFrom(xi)%3))".law(ORDERLY.!) +"x sameType x.`flatMap`(xi => y.toList.take(intFrom(xi)%3))".law(ORDERLY.!, SPECTYPE.!, CPMH_TYPE_11449.!) "x.`exists`(p) == x.`find`(p).isDefined".law @@ -470,13 +470,13 @@ x.forall{ case (k, v) => } """.law(MAP) -"x sameType x.`++`(y)".law +"x sameType x.`++`(y)".law(ACC_SPEC.!, CPMH_TYPE_11449.!) "x.`buffered` sameAs x".law "x.`collect`(pf) sameAs x.`filter`(pf.isDefinedAt).`map`(pf)".law(BITSET_MAP_AMBIG.!) -"x sameType x.`collect`(pf)".law(ORDERLY.!, BITSET_MAP_AMBIG.!) +"x sameType x.`collect`(pf)".law(ORDERLY.!, BITSET_MAP_AMBIG.!, SPECTYPE.!, CPMH_TYPE_11449.!) "x.`contains`(a) == x.`exists`(_ == a)".law(MAP.!) @@ -492,7 +492,7 @@ x.forall{ case (k, v) => "x.`drop`(n) partOf x".law -"x sameType x.`drop`(n)".law/*(CAMEL_WEAKMAP_SUPER.!)*/ +"x sameType x.`drop`(n)".law """ val c = x.`dropWhile`(p); @@ -509,8 +509,7 @@ y.nonEmpty implies y.exists(yi => !p(yi)) """x.`dropWhile`(p) partOf x""".law -"x sameType x.`dropWhile`(p)".law/*(CAMEL_WEAKMAP_SUPER.!)*/ - +"x sameType x.`dropWhile`(p)".law """ val (x1,x2) = x.`duplicate` x1.`corresponds`(x)(_ == _) && x2.corresponds(x)(_ == _) @@ -523,7 +522,7 @@ val (x1,x2) = x.`duplicate` "x.`filterNot`(p) sameAs x.`filter`(xi => !p(xi))".law -"x sameType x.`filterNot`(p)".law/*(CAMEL_WEAKMAP_SUPER.!)*/ +"x sameType x.`filterNot`(p)".law "x.`getOrElse`(a._1, a._2) == x.`get`(a._1).getOrElse(a._2)".law(MAP) @@ -557,7 +556,7 @@ val (x1,x2) = x.`duplicate` "(n <= x.`size`) implies (x.`padTo`(n, a) sameAs x)".law -"x sameType x.`padTo`(n,a)".law +"x sameType x.`padTo`(n,a)".law(SPECTYPE.!) """ val (t,f) = x.`partition`(p) @@ -600,7 +599,7 @@ val (l, r) = x.`partitionMap`(xi => if (p(xi)) Left(f(xi)) else Right(xi)) "n < 0 || m >= x.size || { x.`slice`(n, m) sameAs x.`drop`(n).`take`((0 max m)-n) }".law(SET.!) -"n < 0 || m >= x.size || (x sameType x.`slice`(n, m))".law/*(CAMEL_WEAKMAP_SUPER.!)*/ +"n < 0 || m >= x.size || (x sameType x.`slice`(n, m))".law "x.`span`(p)._1.`forall`(p)".law @@ -613,7 +612,7 @@ val (l, r) = x.`partitionMap`(xi => if (p(xi)) Left(f(xi)) else Right(xi)) """ val (x1, x2) = x.`span`(p) (x1 sameType x2) && (x sameType x1) -""".law/*(CAMEL_WEAKMAP_SUPER.!)*/ +""".law "x.`take`(n).`size` == ((0 max n) min x.size)".law @@ -633,7 +632,7 @@ val (x1, x2) = x.`span`(p) "x.`zip`(y).map(_._2) sameAs y.take(x.size min y.size)".law(BITSET_ZIP_AMBIG.!) -"x sameType x.`zip`(y).map(_._1)".law(MAP.!, ORDERLY.!) +"x sameType x.`zip`(y).map(_._1)".law(MAP.!, ORDERLY.!, SPECTYPE.!) "x.`zipAll`(y, a, f(a)).map(_._1) sameAs x.`padTo`(x.`size` max y.size, a)".law @@ -660,13 +659,13 @@ val zip = x.`zipAll`(y, a, f(a)) sameAs zip """.law(SEQ) -"x sameType x.`zipAll`(y, a, f(a)).map(_._1)".law(MAP.!, ORDERLY.!) +"x sameType x.`zipAll`(y, a, f(a)).map(_._1)".law(MAP.!, ORDERLY.!, SPECTYPE.!) "x.`zipWithIndex`.map(_._1) sameAs x".law "x.`zipWithIndex`.map(_._2) sameAs (0 until x.`size`)".law -"x sameType x.`zipWithIndex`.map(_._1)".law(MAP.!, ORDERLY.!) +"x sameType x.`zipWithIndex`.map(_._1)".law(MAP.!, ORDERLY.!, SPECTYPE.!) /* // The :++ method does not actually exist @@ -675,6 +674,8 @@ x.`zipAll`(y, a, f(a)) sameAs zip "x.`++:`(y) sameAs y.`++`(x)".law(SEQ) +"(x.`++:`(y) samePieces y.`++`(x)) || (x.`++:`(y) samePieces x.`++`(y))".law(SEQ.!) + "x.`++:`(y) sameType y.`++`(x)".law(SEQ) "x.`::`(a).`size` == x.size+1".law @@ -687,13 +688,13 @@ x.`zipAll`(y, a, f(a)) sameAs zip "x.`+:`(a).`head` == a".law -"x sameType x.`+:`(a)".law +"x sameType x.`+:`(a)".law(SPECTYPE.!) "x.`:+`(a).`size` == x.size+1".law "x.`:+`(a).`last` == a".law -"x sameType x.`:+`(a)".law +"x sameType x.`:+`(a)".law(SPECTYPE.!) "val s = x.`+`(a).`size` - x.size; 0 <= s && s <= 1".law @@ -701,7 +702,7 @@ x.`zipAll`(y, a, f(a)) sameAs zip "x.`+`(a).`contains`(a._1)".law(MAP) -"x sameType x.`+`(a)".law +"x sameType x.`+`(a)".law(CPMH_TYPE_11449.!) "x.`:::`(y) sameAs y.`++`(x)".law @@ -933,7 +934,7 @@ x.`reverse`.`forall`{ xi => ki += 1; xi == ix(k - ki) } "x.`reverseMap`(f) sameAs x.map(f).`reverse`".law(SEQ) -"x sameType x.`reverseMap`(f)".law +"x sameType x.`reverseMap`(f)".law(SPECTYPE.!) "x.`reverse_:::`(y) sameAs x.`:::`(y.`reverse`)".law(SEQ) @@ -1071,7 +1072,7 @@ x.`unsorted`.map(a => collectionFrom(Array.fill(n)(a))).`transpose`.`forall`(_ s "x.`union`(y).`toSet` == (x.toSet union y.toSet)".law -"x sameType x.`union`(y)".law +"x sameType x.`union`(y)".law(ACC_SPEC.!) """ val xa = x.`zip`(y).`unzip`._1.`toArray` @@ -1249,9 +1250,9 @@ x0 sameAs x.map{ case (a, b) => a -> f((a, b))._2 } "x.`updated`(a._1, a._2).`forall`{ case (k,v) => if (k == a._1) v == a._2 else x.`get`(k).exists(_ == v) }".law(MAP) -"x sameType x.`updated`(n,a)".law(SEQ, Filt.xsize(_ > 0)) +"x sameType x.`updated`(n,a)".law(SEQ, SPECTYPE.!, Filt.xsize(_ > 0)) -"x sameType x.`updated`(a._1, a._2)".law(MAP, Filt.xsize(_ > 0)) +"x sameType x.`updated`(a._1, a._2)".law(MAP, CPMH_TYPE_11449.!, Filt.xsize(_ > 0)) "{ val x0 = x; x0.`update`(n, a); x0 sameAs x.`updated`(n,a) }".law(MAP.!, Filt.xsize(_ > 0)) @@ -1377,6 +1378,89 @@ gm.forall{ case (k, vs) => m(k).reverse sameAs vs } "x.`knownSize` match { case n if n >= 0 => n == x.`size`; case _ => true }".law +""" +var touched = 0 +var ll = LazyList.from(0).map{ i => touched += 1; i } +(x.`lazyZip`(ll).take(n).map(_._1) samePieces x.`take`(n)) && (touched == (n max 0)) +""".law + +""" +val i = x.`iterator` +val s = x.`stepper` +var same = true +while (same && i.hasNext && s.hasStep) same = i.next == s.nextStep +!i.hasNext && !s.hasStep && same +""".law(STAGGER.!) + +""" +val s = x.`stepper` +val k = x.`keyStepper` +var same = true +while (same && s.hasStep && k.hasStep) same = s.nextStep._1 == k.nextStep +!s.hasStep && !k.hasStep && same +""".law(STAGGER.!) + +""" +val s = x.`stepper` +val v = x.`valueStepper` +var same = true +while (same && s.hasStep && v.hasStep) same = s.nextStep._2 == v.nextStep +!s.hasStep && !v.hasStep && same +""".law(STAGGER.!) + + +""" +val s = x.`stepper` +val built = Array.newBuilder[A] +while (s.hasStep) built += s.nextStep +built.result samePieces x +""".law + +"val s = x.`stepper`; val e: collection.Stepper.EfficientSplit = s; e ne null".law(SPLITS, INEFFICIENT_BUG.!) + +""" +val s = x.`stepper` +val ss = s.trySplit +val built = Array.newBuilder[A] +if (ss != null) while (ss.hasStep) built += ss.nextStep +while (s.hasStep) built += s.nextStep +built.result sameAs x +""".law(SEQ) + +""" +val s = x.`stepper` +val ss = s.trySplit +val built = Array.newBuilder[A] +if (ss != null) while (ss.hasStep) built += ss.nextStep +while (s.hasStep) built += s.nextStep +built.result samePieces x +""".law(SEQ.!) + +""" +val s = x.`stepper` +val ss = s.trySplit +val built = Array.newBuilder[A] +if (ss != null) while (ss.hasStep) built += ss.nextStep +while (s.hasStep) built += s.nextStep +built.result samePieces x +""".law(SEQ.!) + +""" +val built = Array.newBuilder[A] +val xx = x.`tapEach`(built += _) +xx sameAs built.result +""".law(SEQ, ONCE.!, INDEF.!, VIEW.!) + +""" +val built = Array.newBuilder[A] +val xx = x.`tapEach`(built += _) +xx samePieces built.result +""".law(SEQ.!, ONCE.!, INDEF.!, VIEW.!) + +"x sameType x.`tapEach`(_ => ())".law + +"x.`findLast`(p) == x.`reverse`.`find`(p)".law + ///////////////////////////// // End of individual laws. // diff --git a/laws/src/main/scala/Test.scala b/laws/src/main/scala/Test.scala index bb36af1..92371b9 100644 --- a/laws/src/main/scala/Test.scala +++ b/laws/src/main/scala/Test.scala @@ -243,7 +243,11 @@ object Test { } def from[A](iterable: Iterable[A]): Once[A] = from(iterable.iterator) - object Conversions { + trait LowPriorityConversions { + implicit def onceViaAccumulator[A, CC[X] <: collection.mutable.Seq[X]](acc: scala.jdk.Accumulator[A, CC, _]): Once[A] = + Once from acc.iterator + } + object Conversions extends LowPriorityConversions { implicit def onceViaString(string: String): Once[Char] = Once from string implicit def onceViaArray[A](array: Array[A]): Once[A] = Once from array implicit def onceViaIterableOnce[A, CC[A] <: collection.IterableOnce[A]](me: CC[A]): Once[A] =