Skip to content

Commit 1280b93

Browse files
committed
Finished tests of basic steppers and fixed bugs in TryStepper.
1 parent 7866b35 commit 1280b93

File tree

2 files changed

+59
-18
lines changed

2 files changed

+59
-18
lines changed

src/main/scala/scala/compat/java8/collectionImpl/Stepper.scala

+11-4
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,13 @@ trait StepperLike[@specialized(Double, Int, Long) A, +CC] { self =>
8686
////
8787

8888
/** Consumes all remaining elements in this `Stepper` and counts how many there are.
89-
* This is a terminal operation.
89+
* This is a terminal operation, though if `knownSize` is non-negative, it won't actually
90+
* iterate over the elements.
9091
*/
91-
def count(): Long = { var n = 0L; while (hasStep) { nextStep; n += 1 }; n }
92+
def count(): Long = knownSize match {
93+
case x if x < 0 => var n = 0L; while (hasStep) { nextStep; n += 1 }; n
94+
case x => x
95+
}
9296

9397
/** Consumes all remaining elements in this `Stepper` and counts how many satisfy condition `p`.
9498
* This is a terminal operation.
@@ -161,8 +165,9 @@ private[collectionImpl] class ProxySpliteratorViaNext[A](underlying: NextStepper
161165
}
162166

163167
/** This trait indicates that a `Stepper` will implement `hasNext` and `nextStep` by caching applications of `tryStep`.
164-
* Subclasses must implement `tryUncached` instead of `tryStep`, and should leave it protected.
165-
* For speed, `foreachUncached` may also be overridden.
168+
* Subclasses must implement `tryUncached` instead of `tryStep`, and should leave it protected, and must implement
169+
* `knownUncachedSize` instead of `knownSize`. For speed, `foreachUncached` may also be overridden. It is recommended
170+
* that all of the `Uncached` methods be left protected.
166171
*/
167172
trait TryStepper[@specialized(Double, Int, Long) A] extends Stepper[A] with StepperLike[A, TryStepper[A]] {
168173
protected def myCache: A
@@ -183,6 +188,8 @@ trait TryStepper[@specialized(Double, Int, Long) A] extends Stepper[A] with Step
183188
myCache = null.asInstanceOf[A]
184189
ans
185190
}
191+
final def knownSize = knownUncachedSize + (if (myCacheIsFull) 1 else 0)
192+
protected def knownUncachedSize: Long
186193
final def tryStep(f: A => Unit): Boolean = if (myCacheIsFull) { f(myCache); myCacheIsFull = false; true } else tryUncached(f)
187194
protected def tryUncached(f: A => Unit): Boolean
188195
final override def foreach(f: A => Unit) { if (myCacheIsFull) { f(myCache); myCacheIsFull = false }; foreachUncached(f) }

src/test/scala/scala/compat/java8/StepperTest.scala

+48-14
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ class IncStepperA(private val size0: Long) extends NextStepper[Int] {
1515
def knownSize = math.max(0L, size0 - i)
1616
def hasStep = i < size0
1717
def nextStep() = { i += 1; (i - 1).toInt }
18-
def substep() = if ((knownSize - i) <= 1) null else {
19-
val sub = new IncStepperA(i + (size0 - i)/2)
18+
def substep() = if (knownSize <= 1) null else {
19+
val sub = new IncStepperA(size0 - (size0 - i)/2)
2020
sub.i = i
2121
i = sub.size0
2222
sub
@@ -29,9 +29,9 @@ class IncStepperB(private val size0: Long) extends TryStepper[Int] {
2929
protected var myCache: Int = 0
3030
private var i = 0L
3131
def characteristics = Stepper.Sized | Stepper.SubSized | Stepper.Ordered
32-
def knownSize = math.max(0L, size0 - i)
32+
def knownUncachedSize = math.max(0L, size0 - i)
3333
protected def tryUncached(f: Int => Unit): Boolean = if (i >= size0) false else { f(i.toInt); i += 1; true }
34-
def substep() = if ((knownSize - i) <= 1) null else {
34+
def substep() = if (knownSize <= 1) null else {
3535
val sub = new IncStepperB(size0 - (size0 - i)/2)
3636
sub.i = i
3737
i = sub.size0
@@ -41,6 +41,7 @@ class IncStepperB(private val size0: Long) extends TryStepper[Int] {
4141
}
4242

4343
class IncSpliterator(private val size0: Long) extends Spliterator.OfInt {
44+
if (size0 < 0) throw new IllegalArgumentException("Size must be >= 0L")
4445
private var i = 0L
4546
def characteristics() = Stepper.Sized | Stepper.SubSized | Stepper.Ordered
4647
def estimateSize() = math.max(0L, size0 - i)
@@ -123,15 +124,27 @@ class StepperTest {
123124
}
124125

125126
val sizes = Vector(0, 1, 2, 4, 15, 17, 2512)
126-
def sources: Vector[(Int, Stepper[Int])] = sizes.flatMap(i => Vector(
127-
i -> new IncStepperA(i),
128-
i -> new IncStepperB(i),
129-
i -> Stepper.ofSpliterator(new IncSpliterator(i)),
130-
i -> new MappingStepper[Int,Int](new IncStepperA(i), x => x),
131-
i -> new MappingStepper[Long, Int](Stepper.ofSpliterator(new IntToLongSpliterator(new IncSpliterator(i), _.toLong)), _.toInt),
132-
i -> new MappingStepper[Double, Int](Stepper.ofSpliterator(new IntToDoubleSpliterator(new IncSpliterator(i), _.toDouble)), _.toInt),
133-
i -> new MappingStepper[String, Int](Stepper.ofSpliterator(new IntToGenericSpliterator[String](new IncSpliterator(i), _.toString)), _.toInt)
134-
))
127+
def sources: Vector[(Int, Stepper[Int])] = sizes.flatMap{ i =>
128+
Vector(
129+
i -> new IncStepperA(i),
130+
i -> new IncStepperB(i),
131+
i -> Stepper.ofSpliterator(new IncSpliterator(i)),
132+
i -> new MappingStepper[Int,Int](new IncStepperA(i), x => x),
133+
i -> new MappingStepper[Long, Int](Stepper.ofSpliterator(new IntToLongSpliterator(new IncSpliterator(i), _.toLong)), _.toInt),
134+
i -> new MappingStepper[Double, Int](Stepper.ofSpliterator(new IntToDoubleSpliterator(new IncSpliterator(i), _.toDouble)), _.toInt),
135+
i -> new MappingStepper[String, Int](Stepper.ofSpliterator(new IntToGenericSpliterator[String](new IncSpliterator(i), _.toString)), _.toInt)
136+
) ++
137+
{
138+
// Implicitly converted instead of explicitly
139+
import SpliteratorConverters._
140+
Vector[(Int, Stepper[Int])](
141+
i -> (new IncSpliterator(i)).stepper,
142+
i -> new MappingStepper[Long, Int]((new IntToLongSpliterator(new IncSpliterator(i), _.toLong)).stepper, _.toInt),
143+
i -> new MappingStepper[Double, Int]((new IntToDoubleSpliterator(new IncSpliterator(i), _.toDouble)).stepper, _.toInt),
144+
i -> new MappingStepper[String, Int]((new IntToGenericSpliterator[String](new IncSpliterator(i), _.toString)).stepper, _.toInt)
145+
)
146+
}
147+
}
135148

136149
@Test
137150
def stepping() {
@@ -177,12 +190,33 @@ class StepperTest {
177190
if (ss != null) {
178191
assertTrue(s.hasStep)
179192
assertTrue(ss.hasStep)
180-
assertEquals(i, s.count + ss.count)
193+
val c1 = s.count
194+
val c2 = ss.count
195+
assertEquals(s"$i != $c1 + $c2 from ${s.getClass.getName}", i, c1 + c2)
181196
}
182197
else assertEquals(i, s.count)
183198
}
184199
}
185200

201+
@Test
202+
def characteristically() {
203+
val expected = Stepper.Sized | Stepper.SubSized | Stepper.Ordered
204+
sources.foreach{ case (_,s) => assertEquals(s.characteristics, expected)}
205+
sources.foreach{ case (_,s) => subs(0)(s)(x => { assertEquals(x.characteristics, expected); 0 }, _ + _) }
206+
}
207+
208+
@Test
209+
def knownSizes() {
210+
sources.foreach{ case (i,s) => assertEquals(i.toLong, s.knownSize) }
211+
sources.foreach{ case (i,s) => if (i > 0) subs(0)(s)(x => { assertEquals(x.knownSize, 1L); 0 }, _ + _) }
212+
}
213+
214+
@Test
215+
def consistentPrecision() {
216+
sources.foreach{ case (_,s) => assert(s eq s.typedPrecisely) }
217+
sources.foreach{ case (_,s) => subs(0)(s)(x => { assert(x eq x.typedPrecisely); 0}, _ + _) }
218+
}
219+
186220
@Test
187221
def count_only() {
188222
sources.foreach{ case (i, s) => assertEquals(i, s.count) }

0 commit comments

Comments
 (0)