Skip to content

Commit b803e31

Browse files
committed
1 parent 18e2487 commit b803e31

File tree

2 files changed

+53
-4
lines changed

2 files changed

+53
-4
lines changed

src/library/scala/collection/immutable/NumericRange.scala

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,27 @@ sealed class NumericRange[T](
138138
// based on the given value.
139139
private def newEmptyRange(value: T) = NumericRange(value, value, step)
140140

141+
// Calling length when a range contains more than Int.MaxValue causes IllegalArgumentException
142+
// because the length of the range cannot be represented with Int numbers.
143+
// This method tries to compare the length of this with the given Int number.
144+
private def compareLength(n: Int): Int = {
145+
val diff = num.minus(end, start)
146+
val thisLength = num.quot(diff, step)
147+
num.compare(num.fromInt(n), thisLength)
148+
}
149+
141150
override def take(n: Int): NumericRange[T] = {
142151
if (n <= 0 || isEmpty) newEmptyRange(start)
143-
else if (n >= length) this
144-
else new NumericRange.Inclusive(start, locationAfterN(n - 1), step)
152+
else if (compareLength(n) < 0)
153+
new NumericRange.Inclusive(start, locationAfterN(n - 1), step)
154+
else this
145155
}
146156

147157
override def drop(n: Int): NumericRange[T] = {
148158
if (n <= 0 || isEmpty) this
149-
else if (n >= length) newEmptyRange(end)
150-
else copy(locationAfterN(n), end, step)
159+
else if (compareLength(n) < 0)
160+
copy(locationAfterN(n), end, step)
161+
else newEmptyRange(end)
151162
}
152163

153164
override def splitAt(n: Int): (NumericRange[T], NumericRange[T]) = (take(n), drop(n))

test/junit/scala/collection/immutable/NumericRangeTest.scala

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,42 @@ class NumericRangeTest {
157157
assertEquals(Nil, check(testDecreaseCase.start to testIncreaseCase.last by testIncreaseCase.step))
158158
assertEquals(Nil, check(testDecreaseCase.inclusive))
159159
}
160+
161+
@Test
162+
def numericRangeWithMoreThanMaxIntTake() = {
163+
val start = BigInt(0)
164+
val step = BigInt(1)
165+
val end = BigInt(Int.MaxValue) * 5
166+
167+
// evaluation of length causes IllegalArgumentException because it cannot be represented as Int number
168+
// Yet using `take` should be independent of evaluating length when it's not necessary.
169+
assertThrows[IllegalArgumentException]((start until end by step).length)
170+
val range = start until end by step
171+
172+
val take = 100
173+
val smallChunkOfRange = range.take(take)
174+
val expected = start until BigInt(100) by step
175+
assertTrue(smallChunkOfRange equals expected)
176+
assertTrue(smallChunkOfRange.length == take)
177+
}
178+
179+
@Test
180+
def numericRangeWithMoreThanMaxIntDrop() = {
181+
val start = BigInt(0)
182+
val step = BigInt(1)
183+
val toAddToMaxInt = 50
184+
val end = BigInt(Int.MaxValue) + toAddToMaxInt
185+
186+
val drop = Int.MaxValue
187+
188+
// evaluation of length causes IllegalArgumentException because it cannot be represented as Int number
189+
// Yet using `drop` should be independent of evaluating length when it's not necessary.
190+
assertThrows[IllegalArgumentException]((start until end by step).length)
191+
val range = start until end by step
192+
193+
val smallChunkOfRange = range.drop(drop)
194+
val expected = BigInt(Int.MaxValue) until end by step
195+
assertTrue(smallChunkOfRange equals expected)
196+
assertTrue(smallChunkOfRange.length == toAddToMaxInt)
197+
}
160198
}

0 commit comments

Comments
 (0)