stdlib: Add BitSet implementation.

This commit is contained in:
Ilya Matveev
2017-06-06 17:42:02 +07:00
committed by ilmat192
parent 00dc4524ca
commit 6ff43d690f
3 changed files with 862 additions and 0 deletions
+5
View File
@@ -949,6 +949,11 @@ task AbstractMutableCollection(type: RunKonanTest) {
source = "runtime/collections/AbstractMutableCollection.kt"
}
task BitSet(type: RunKonanTest) {
expectedExitStatus = 0
source = "runtime/collections/BitSet.kt"
}
task array0(type: RunKonanTest) {
goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n"
source = "runtime/collections/array0.kt"
@@ -0,0 +1,417 @@
fun assertContainsOnly(bitSet: BitSet, trueBits: Set<Int>, size: Int) {
for (i in 0 until size) {
val expectedBit = i in trueBits
if (bitSet[i] != expectedBit) {
throw AssertionError()
}
}
}
fun assertNotContainsOnly(bitSet: BitSet, falseBits: Set<Int>, size: Int) {
for (i in 0 until size) {
val expectedBit = i !in falseBits
if (bitSet[i] != expectedBit) {
throw AssertionError()
}
}
}
fun assertTrue(str: String, cond: Boolean) { if (!cond) throw AssertionError(str) }
fun assertFalse(str: String, cond: Boolean) { if (cond) throw AssertionError(str) }
fun <T> assertEquals(str: String, a: T, b: T) { if (a != b) throw AssertionError(str) }
fun assertTrue(cond: Boolean) = assertTrue("", cond)
fun assertFalse(cond: Boolean) = assertFalse("", cond)
fun <T> assertEquals(a: T, b: T) = assertEquals("", a, b)
fun fail(): Unit = throw AssertionError()
fun testConstructor() {
var b = BitSet(12)
assertContainsOnly(b, setOf(), 12)
b = BitSet(12) { it == 0 || it in 5..6 || it == 11 }
assertContainsOnly(b, setOf(0, 5, 6, 11), 12)
}
fun testSet() {
var b = BitSet(0)
assertEquals(b.lastTrueIndex, -1)
b = BitSet(2)
// Test set and clear operation for single index.
assertTrue(b.isEmpty)
b.set(0, true)
b.set(3, true)
assertEquals(b.lastTrueIndex, 3)
assertFalse(b.isEmpty)
assertContainsOnly(b, setOf(0, 3), 4)
b.clear()
assertContainsOnly(b, setOf(), 4)
b.set(1)
b.set(5)
assertEquals(b.lastTrueIndex, 5)
assertContainsOnly(b, setOf(1, 5), 6)
b.set(1, false)
b.set(7, false)
assertEquals(b.lastTrueIndex, 5)
assertContainsOnly(b, setOf(5), 8)
b.clear(5)
assertEquals(b.lastTrueIndex, -1)
assertContainsOnly(b, setOf(), 8)
b.set(70)
assertEquals(b.lastTrueIndex, 70)
assertContainsOnly(b, setOf(70), 71)
b.clear(70)
assertEquals(b.lastTrueIndex, -1)
assertContainsOnly(b, setOf(), 71)
// Test set and clear operations for ranges.
// Set false and clear.
b = BitSet(2)
assertContainsOnly(b, setOf(), 2)
b.set(0..70, true)
assertNotContainsOnly(b, setOf(), 71)
b.set(0..2, false)
assertNotContainsOnly(b, setOf(0, 1, 2), 71)
b.set(63..65, false)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65), 71)
b.set(68..70, false)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 68, 69, 70), 71)
b.set(68..72, false)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 68, 69, 70, 71, 72), 73)
b.set(0..72, false)
assertContainsOnly(b, setOf(), 73)
b.set(0..72)
assertNotContainsOnly(b, setOf(), 73)
b.clear(0..2)
assertNotContainsOnly(b, setOf(0, 1, 2), 73)
b.clear(63..65)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65), 73)
b.clear(68..70)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 68, 69, 70), 73)
b.clear(68..72)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 68, 69, 70, 71, 72), 73)
b.clear(0..72)
assertContainsOnly(b, setOf(), 73)
// Set true.
b.set(0..2, true)
assertContainsOnly(b, setOf(0, 1, 2), 73)
b.set(63..65, true)
assertContainsOnly(b, setOf(0, 1, 2, 63, 64, 65), 73)
b.set(70..72, true)
assertContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 70, 71, 72), 73)
b.set(73..74, true)
assertContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 70, 71, 72, 73, 74), 75)
b.set(0..74, true)
assertNotContainsOnly(b, setOf(), 75)
// Test set and clear for pair of indices.
b = BitSet(2)
assertContainsOnly(b, setOf(), 71)
b.set(0, 71, true)
assertNotContainsOnly(b, setOf(), 71)
b.set(0, 3, false)
assertNotContainsOnly(b, setOf(0, 1, 2), 71)
b.set(63, 66, false)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65), 71)
b.set(68, 71, false)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 68, 69, 70), 71)
b.set(68, 73, false)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 68, 69, 70, 71, 72), 73)
b.set(0, 73, false)
assertContainsOnly(b, setOf(), 73)
b.set(0, 73)
assertNotContainsOnly(b, setOf(), 73)
b.clear(0, 3)
assertNotContainsOnly(b, setOf(0, 1, 2), 73)
b.clear(63, 66)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65), 73)
b.clear(68, 71)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 68, 69, 70), 73)
b.clear(68, 73)
assertNotContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 68, 69, 70, 71, 72), 73)
b.clear(0, 73)
assertContainsOnly(b, setOf(), 73)
// Set true.
b.set(0, 3, true)
assertContainsOnly(b, setOf(0, 1, 2), 73)
b.set(63, 66, true)
assertContainsOnly(b, setOf(0, 1, 2, 63, 64, 65), 73)
b.set(70, 73, true)
assertContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 70, 71, 72), 73)
b.set(73, 75, true)
assertContainsOnly(b, setOf(0, 1, 2, 63, 64, 65, 70, 71, 72, 73, 74), 75)
b.set(0, 75, true)
assertNotContainsOnly(b, setOf(), 75)
// Access to negative elements must cause an exception
try {
b.set(-1)
fail()
} catch(e: IndexOutOfBoundsException) {}
try {
b.clear(-1)
fail()
} catch(e: IndexOutOfBoundsException) {}
try {
b.clear(-1..0)
fail()
} catch(e: IndexOutOfBoundsException) {}
try {
b.set(-1..0)
fail()
} catch(e: IndexOutOfBoundsException) {}
try {
b[-1]
fail()
} catch(e: IndexOutOfBoundsException) {}
}
fun testFlip() {
val b = BitSet(2)
b.set(0, true)
b.set(70, true)
b.set(63..65, true)
assertEquals(b.lastTrueIndex, 70)
// 0 element
assertContainsOnly(b, setOf(0, 63, 64, 65, 70), 71)
b.flip(0)
assertContainsOnly(b, setOf(63, 64, 65, 70), 71)
b.flip(1)
assertContainsOnly(b, setOf(1, 63, 64, 65, 70), 71)
b.flip(0)
assertContainsOnly(b, setOf(0, 1, 63, 64, 65, 70), 71)
// last element
b.flip(70)
assertContainsOnly(b, setOf(0, 1, 63, 64, 65), 71)
b.flip(69)
assertContainsOnly(b, setOf(0, 1, 63, 64, 65, 69), 71)
b.flip(70)
assertContainsOnly(b, setOf(0, 1, 63, 64, 65, 69, 70), 71)
// element in the middle
b.flip(64)
assertContainsOnly(b, setOf(0, 1, 63, 65, 69, 70), 71)
b.flip(65)
assertContainsOnly(b, setOf(0, 1, 63, 69, 70), 71)
b.flip(65)
b.flip(64)
assertContainsOnly(b, setOf(0, 1, 63, 64, 65, 69, 70), 71)
// range in the beginning
b.flip(0..2)
assertContainsOnly(b, setOf(2, 63, 64, 65, 69, 70), 71)
b.flip(0, 3)
assertContainsOnly(b, setOf(0, 1, 63, 64, 65, 69, 70), 71)
// In the end
b.flip(68..70)
assertContainsOnly(b, setOf(0, 1, 63, 64, 65, 68), 71)
b.flip(68, 71)
assertContainsOnly(b, setOf(0, 1, 63, 64, 65, 69, 70), 71)
// In the middle
b.flip(64..66)
assertContainsOnly(b, setOf(0, 1, 63, 66, 69, 70), 71)
b.flip(64, 67)
assertContainsOnly(b, setOf(0, 1, 63, 64, 65, 69, 70), 71)
// Access to a negative element must cause an exception.
try {
b.flip(-1)
fail()
} catch(e: IndexOutOfBoundsException) {}
try {
b.flip(-1..0)
fail()
} catch(e: IndexOutOfBoundsException) {}
}
fun testNextBit() {
val b = BitSet(71)
b.set(0)
b.set(65)
b.set(70)
assertEquals(b.nextSetBit(), 0)
assertEquals(b.nextSetBit(0), 0)
assertEquals(b.nextSetBit(1), 65)
assertEquals(b.nextSetBit(65), 65)
assertEquals(b.nextSetBit(66), 70)
assertEquals(b.nextSetBit(70), 70)
assertEquals(b.nextSetBit(71), -1)
assertEquals(b.previousSetBit(0), 0)
assertEquals(b.previousSetBit(64), 0)
assertEquals(b.previousSetBit(65), 65)
assertEquals(b.previousSetBit(69), 65)
assertEquals(b.previousSetBit(70), 70)
assertEquals(b.previousSetBit(71), 70)
b.clear()
assertEquals(b.nextSetBit(), -1)
assertEquals(b.previousSetBit(70), -1)
b.set(0..70)
assertEquals(b.nextClearBit(), 71)
assertEquals(b.previousClearBit(70), -1)
b.clear(0)
b.clear(65)
b.clear(70)
assertEquals(b.nextClearBit(), 0)
assertEquals(b.nextClearBit(0), 0)
assertEquals(b.nextClearBit(1), 65)
assertEquals(b.nextClearBit(65), 65)
assertEquals(b.nextClearBit(66), 70)
assertEquals(b.nextClearBit(70), 70)
assertEquals(b.nextClearBit(71), 71) // assume that the bitset is extended here (virtually).
assertEquals(b.previousClearBit(0), 0)
assertEquals(b.previousClearBit(64), 0)
assertEquals(b.previousClearBit(65), 65)
assertEquals(b.previousClearBit(69), 65)
assertEquals(b.previousClearBit(70), 70)
assertEquals(b.previousClearBit(71), 71)
// See http://docs.oracle.com/javase/8/docs/api/java/util/BitSet.html#previousClearBit-int-
assertEquals(b.previousClearBit(-1), -1)
assertEquals(b.previousSetBit(-1), -1)
// Test behaviour on the right border of the bit vector.
// We assume that the vector is infinite and have zeros after (size - 1)th bit.
var a = BitSet(64)
assertEquals(a.nextClearBit(63), 63)
assertEquals(a.nextClearBit(64), 64)
assertEquals(a.nextSetBit(63), -1)
assertEquals(a.nextSetBit(64), -1)
a.set(0, 64)
assertEquals(a.nextClearBit(63), 64)
assertEquals(a.nextClearBit(64), 64)
assertEquals(a.nextSetBit(63), 63)
assertEquals(a.nextSetBit(64), -1)
a.clear()
assertEquals(a.previousClearBit(63), 63)
assertEquals(a.previousClearBit(64), 64)
assertEquals(a.previousSetBit(63), -1)
assertEquals(a.previousSetBit(64), -1)
a.set(0, 64)
assertEquals(a.previousClearBit(63), -1)
assertEquals(a.previousClearBit(64), 64)
assertEquals(a.previousSetBit(63), 63)
assertEquals(a.previousSetBit(64), 63)
a = BitSet(0)
assertEquals(a.nextSetBit(0), -1)
assertEquals(a.nextClearBit(0), 0)
assertEquals(a.previousSetBit(0), -1)
assertEquals(a.previousClearBit(0), 0)
// Access to a negative element must cause an exception.
try {
b.previousSetBit(-2)
fail()
} catch(e: IndexOutOfBoundsException) {}
try {
b.previousClearBit(-2)
fail()
} catch(e: IndexOutOfBoundsException) {}
try {
b.nextSetBit(-1)
fail()
} catch(e: IndexOutOfBoundsException) {}
try {
b.nextClearBit(-1)
fail()
} catch(e: IndexOutOfBoundsException) {}
}
fun BitSet.setBits(vararg indices: Int, value: Boolean = true) {
indices.forEach {
set(it, value)
}
}
fun testLogic() {
var b2 = BitSet(76)
b2.setBits(1, 3,
61, 63,
65, 67,
70, 72)
var b1 = BitSet(73)
b1.set(2..3); b1.set(62..63); b1.set(66..67); b1.set(71..72)
b1.and(b2)
assertContainsOnly(b1, setOf(3, 63, 67, 72), 76)
b1 = BitSet(73)
b1.set(2..3); b1.set(62..63); b1.set(66..67); b1.set(71..72)
b1.or(b2)
assertContainsOnly(b1, setOf(1, 2, 3, 61, 62, 63, 65, 66, 67, 70, 71, 72), 76)
b1 = BitSet(73)
b1.set(2..3); b1.set(62..63); b1.set(66..67); b1.set(71..72)
b1.xor(b2)
assertContainsOnly(b1, setOf(1, 2, 61, 62, 65, 66, 70, 71), 76)
b1 = BitSet(73)
b1.set(2..3); b1.set(62..63); b1.set(66..67); b1.set(71..72)
b1.andNot(b2)
assertContainsOnly(b1, setOf(2, 62, 66, 71), 76)
b1 = BitSet(73)
b1.set(0..1); b1.set(62..63); b1.set(64..65); b1.set(71..72)
b2.clear(); b2.set(0)
assertTrue(b1.intersects(b2))
b2.clear(); b2.set(62..65)
assertTrue(b1.intersects(b2))
b2.clear(); b2.set(72)
assertTrue(b1.intersects(b2))
b2.clear()
assertFalse(b1.intersects(b2))
}
// Based on Harmony tests.
fun testEqualsHashCode() {
// HashCode.
val b = BitSet()
b.set(0..7)
b.clear(2)
b.clear(6)
assertEquals("BitSet returns wrong hash value", 1129, b.hashCode())
b.set(10)
b.clear(3)
assertEquals("BitSet returns wrong hash value", 97, b.hashCode())
// Equals.
val b1 = BitSet()
val b2 = BitSet()
b1.set(0..7)
b2.set(0..7)
assertTrue("Same BitSet returned false", b1 == b1)
assertTrue("Identical BitSet returned false", b1 == b2)
b2.clear(6)
assertFalse("Different BitSets returned true", b1 == b2)
val b3 = BitSet()
b3.set(0..7)
b3.set(128)
assertFalse("Different sized BitSet with higher bit set returned true", b1 == b3)
b3.clear(128)
assertTrue("Different sized BitSet with higher bits not set returned false", b1 == b3)
}
fun main(args: Array<String>) {
testConstructor()
testSet()
testFlip()
testNextBit()
testLogic()
testEqualsHashCode()
}
+440
View File
@@ -0,0 +1,440 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotlin
/**
* A vector of bits growing if necessary and allowing one to set/clear/read bits from it by a bit index.
*/
class BitSet(size: Int = ELEMENT_SIZE) {
companion object {
// Size of one element in the array used to store bits.
private const val ELEMENT_SIZE = 64
private const val MAX_BIT_OFFSET = ELEMENT_SIZE - 1
private const val ALL_TRUE = -1L // 0xFFFF_FFFF_FFFF_FFFF
private const val ALL_FALSE = 0L // 0x0000_0000_0000_0000
}
private var bits: LongArray = LongArray(bitToElementSize(size))
private val lastIndex: Int
get() = size - 1
/** Returns an index of the last bit that has `true` value. Returns -1 if the set is empty. */
val lastTrueIndex: Int
get() = previousSetBit(size)
/** True if this BitSet contains no bits set to true. */
val isEmpty: Boolean
get() = bits.all { it == ALL_FALSE }
/** Actual number of bits available in the set. All bits with indices >= size assumed to be 0 */
var size: Int = size
private set
// TODO: Add more constructors.
constructor(length: Int, initializer: (Int) -> Boolean): this(length) {
for (i in 0 until length) {
set(i, initializer(i))
}
}
// Transforms a bit index into an element index in the `bits` array.
private val Int.elementIndex: Int
get() = this / ELEMENT_SIZE
// Transforms a bit index in the set into a bit in the element of the `bits` array.
private val Int.bitOffset: Int
get() = this % ELEMENT_SIZE
// Transforms a bit index in the set into pair of a `bits` element index and a bit index in the element.
private val Int.asBitCoordinates: Pair<Int, Int>
get() = Pair(elementIndex, bitOffset)
// Transforms a bit offset to the mask with only bit set corresponding to the offset.
private val Int.asMask: Long
get() = 0x1L shl this
// Transforms a bit offset to the mask with only bits before the index (inclusive) set.
private val Int.asMaskBefore: Long
get() = getMaskBetween(0, this)
// Transforms a bit offset to the mask with only bits after the index (inclusive) set.
private val Int.asMaskAfter: Long
get() = getMaskBetween(this, MAX_BIT_OFFSET)
// Builds a masks with 1 between fromOffset and toOffset (both inclusive).
private fun getMaskBetween(fromOffset: Int, toOffset: Int): Long {
var res = 0L
val maskToAdd = fromOffset.asMask
for (i in fromOffset..toOffset) {
res = (res shl 1) or maskToAdd
}
return res
}
// Transforms a size in bits to a size in elements of the `bits` array.
private fun bitToElementSize(bitSize: Int): Int =
if (bitSize % ELEMENT_SIZE == 0) bitSize / ELEMENT_SIZE else bitSize / ELEMENT_SIZE + 1
// Transforms a pair of an element index and a bit offset to a bit index.
private fun bitIndex(elementIndex: Int, bitOffset: Int) =
elementIndex * ELEMENT_SIZE + bitOffset
// Sets all bits after the last available bit (size - 1) to 0.
private fun clearUnusedTail() {
val (lastElementIndex, lastBitOffset) = lastIndex.asBitCoordinates
bits[bits.lastIndex] = bits[bits.lastIndex] and lastBitOffset.asMaskBefore
for (i in lastElementIndex + 1 until bits.size) {
bits[i] = ALL_FALSE
}
}
// Internal function. Sets bits specified by the element index and the given mask to value.
private fun setBitsWithMask(elementIndex: Int, mask: Long, value: Boolean) {
val element = bits[elementIndex]
if (value) {
bits[elementIndex] = element or mask
} else {
bits[elementIndex] = element and mask.inv()
}
}
// Internal function. Flips bits specified by the element index and the given mask.
private fun flipBitsWithMask(elementIndex: Int, mask: Long) {
val element = bits[elementIndex]
bits[elementIndex] = element xor mask
}
/**
* Checks if index is valid and extends the `bits` array if the index exceeds its size.
* Throws [IndexOutOfBoundsException] if [index] < 0.
*/
private fun ensureCapacity(index: Int) {
if (index < 0) {
throw IndexOutOfBoundsException()
}
if (index >= size) {
size = index + 1
if (index.elementIndex >= bits.size) {
// Create a new array containing the index-th bit.
bits = bits.copyOf(bitToElementSize(index + 1))
}
// Set all bits after the index to 0. TODO: We can remove it.
clearUnusedTail()
}
}
/** Set the bit specified to the specified value. */
fun set(index: Int, value: Boolean = true) {
ensureCapacity(index)
val (elementIndex, offset) = index.asBitCoordinates
setBitsWithMask(elementIndex, offset.asMask, value)
}
/** Sets the bits with indices between [from] (inclusive) and [to] (exclusive) to the specified value. */
fun set(from : Int, to: Int, value: Boolean = true) = set(from until to, value)
/** Sets the bits from the range specified to the specified value. */
fun set(range: IntRange, value: Boolean = true) {
if (range.start < 0 || range.endInclusive < 0) {
throw IndexOutOfBoundsException()
}
if (range.start > range.endInclusive) { // Empty range.
return
}
ensureCapacity(range.endInclusive)
val (fromIndex, fromOffset) = range.start.asBitCoordinates
val (toIndex, toOffset) = range.endInclusive.asBitCoordinates
if (toIndex == fromIndex) {
val mask = getMaskBetween(fromOffset, toOffset)
setBitsWithMask(fromIndex, mask, value)
} else {
// Set bits in the first element.
setBitsWithMask(fromIndex, fromOffset.asMaskAfter, value)
// Set all bits of all elements (excluding border ones) to 0 or 1 depending.
for (index in fromIndex + 1 until toIndex) {
bits[index] = if (value) ALL_TRUE else ALL_FALSE
}
// Set bits in the last element
setBitsWithMask(toIndex, toOffset.asMaskBefore, value)
}
}
/** Clears the bit specified */
fun clear(index: Int) = set(index, false)
/** Clears the bits with indices between [from] (inclusive) and [to] (exclusive) to the specified value. */
fun clear(from : Int, to: Int) = set(from, to, false)
/** Clears the bit specified */
fun clear(range: IntRange) = set(range, false)
/** Sets all bits in the BitSet to `false`. */
fun clear() {
for (i in bits.indices) {
bits[i] = ALL_FALSE
}
}
/** Reverses the bit specified. */
fun flip(index: Int) {
ensureCapacity(index)
val (elementIndex, offset) = index.asBitCoordinates
flipBitsWithMask(elementIndex, offset.asMask)
}
/** Reverses the bits with indices between [from] (inclusive) and [to] (exclusive). */
fun flip(from: Int, to: Int) = flip(from until to)
/** Reverses the bits from the range specified. */
fun flip(range: IntRange) {
if (range.start < 0 || range.endInclusive < 0) {
throw IndexOutOfBoundsException()
}
if (range.start > range.endInclusive) { // Empty range.
return
}
ensureCapacity(range.endInclusive)
val (fromIndex, fromOffset) = range.start.asBitCoordinates
val (toIndex, toOffset) = range.endInclusive.asBitCoordinates
if (toIndex == fromIndex) {
val mask = getMaskBetween(fromOffset, toOffset)
flipBitsWithMask(fromIndex, mask)
} else {
// Flip bits in the first element.
flipBitsWithMask(toIndex, toOffset.asMaskAfter)
// Flip bits between the first and the last elements.
for (index in fromIndex + 1 until toIndex) {
bits[index] = bits[index].inv()
}
// Flip bits in the last element.
flipBitsWithMask(toIndex, toOffset.asMaskBefore)
}
}
/**
* Returns an index of a next set (if [lookFor] == true) or clear
* (if [lookFor] == false) bit after [startIndex] (inclusive).
* Returns -1 (for [lookFor] == true) or [size] (for lookFor == false)
* if there is no such bits between [startIndex] and [size] - 1.
* Throws IndexOutOfBoundException if [startIndex] < 0.
*/
private fun nextBit(startIndex: Int, lookFor: Boolean): Int {
if (startIndex < 0) {
throw IndexOutOfBoundsException()
}
if (startIndex >= size) {
return if (lookFor) -1 else startIndex
}
val (startElementIndex, startOffset) = startIndex.asBitCoordinates
// Look for the next set bit in the first element.
var element = bits[startElementIndex]
for (offset in startOffset..MAX_BIT_OFFSET) {
val bit = element and (0x1L shl offset) != 0L
if (bit == lookFor) { // Look for not 0 if we need a set bit and look for 0 otherwise.
return bitIndex(startElementIndex, offset)
}
}
// Look for in the remaining elements.
for (index in startElementIndex + 1..bits.lastIndex) {
element = bits[index]
for (offset in 0..MAX_BIT_OFFSET) {
val bit = element and (0x1L shl offset) != 0L
if (bit == lookFor) { // Look for not 0 if we need a set bit and look for 0 otherwise.
return bitIndex(index, offset)
}
}
}
return if (lookFor) -1 else size
}
/**
* Returns an index of a next bit which value is `true` after [startIndex] (inclusive).
* Returns -1 if there is no such bits after [startIndex].
* Throws IndexOutOfBoundException if [startIndex] < 0.
*/
fun nextSetBit(startIndex: Int = 0): Int = nextBit(startIndex, true)
/**
* Returns an index of a next bit which value is `false` after [startIndex] (inclusive).
* Returns [size] if there is no such bits between [startIndex] and [size] - 1 assuming that the set has an infinite
* sequence of `false` bits after (size - 1)-th.
* Throws IndexOutOfBoundException if [startIndex] < 0.
*/
fun nextClearBit(startIndex: Int = 0): Int = nextBit(startIndex, false)
/**
* Returns the biggest index of a bit which value is [lookFor] before [startIndex] (inclusive).
* Returns -1 if there is no such bits before [startIndex].
* If [startIndex] >= [size] will search from [size] - 1 (if [lookFor] == true)
* or return [startIndex] (if [lookFor == false]).
*/
fun previousBit(startIndex: Int, lookFor: Boolean): Int {
var correctStartIndex = startIndex
if (startIndex >= size) {
// We assume that all bits after `size - 1` are 0. So we can return the start index if we are looking for 0.
if (!lookFor) {
return startIndex
} else {
// If we are looking for 1 we can skip all these 0 after `size - 1`.
correctStartIndex = size - 1
}
}
if (correctStartIndex < -1) {
throw IndexOutOfBoundsException()
}
if (correctStartIndex == -1) {
return -1
}
val (startElementIndex, startOffset) = correctStartIndex.asBitCoordinates
// Look for the next set bit in the first element.
var element = bits[startElementIndex]
for (offset in startOffset downTo 0) {
val bit = element and (0x1L shl offset) != 0L
if (bit == lookFor) { // Look for not 0 if we need a set bit and look for 0 otherwise.
return bitIndex(startElementIndex, offset)
}
}
// Look for in the remaining elements.
for (index in startElementIndex - 1 downTo 0) {
element = bits[index]
for (offset in MAX_BIT_OFFSET downTo 0) {
val bit = element and (0x1L shl offset) != 0L
if (bit == lookFor) { // Look for not 0 if we need a set bit and look for 0 otherwise.
return bitIndex(index, offset)
}
}
}
return -1
}
/**
* Returns the biggest index of a bit which value is `true` before [startIndex] (inclusive).
* Returns -1 if there is no such bits before [startIndex] or if [startIndex] == -1.
* Throws IndexOutOfBoundException if [startIndex] < -1.
*/
fun previousSetBit(startIndex: Int): Int = previousBit(startIndex, true)
/**
* Returns the biggest index of a bit which value is `false` before [startIndex] (inclusive).
* Returns -1 if there is no such bits before [startIndex] or if [startIndex] == -1.
* If [startIndex] >= size will return [startIndex] assuming that the set has an infinite
* sequence of `false` bits after (size - 1)-th.
* Throws IndexOutOfBoundException if [startIndex] < -1.
*/
fun previousClearBit(startIndex: Int): Int = previousBit(startIndex, false)
/** Returns a value of a bit with the [index] specified. */
operator fun get(index: Int): Boolean {
if (index < 0) {
throw IndexOutOfBoundsException()
}
if (index >= size) {
return false
}
val (elementIndex, offset) = index.asBitCoordinates
return bits[elementIndex] and offset.asMask != 0L
}
/** Performs a logical and operation over corresponding bits of this and [another] BitSets. The result is saved in this BitSet. */
fun and(another: BitSet) {
ensureCapacity(another.lastIndex)
for (index in bits.indices) {
bits[index] = bits[index] and another.bits[index]
}
}
/** Performs a logical or operation over corresponding bits of this and [another] BitSets. The result is saved in this BitSet. */
fun or(another: BitSet) {
ensureCapacity(another.lastIndex)
for (index in bits.indices) {
bits[index] = bits[index] or another.bits[index]
}
}
/** Performs a logical and + not operations over corresponding bits of this and [another] BitSets. The result is saved in this BitSet. */
fun andNot(another: BitSet) {
ensureCapacity(another.lastIndex)
for (index in bits.indices) {
bits[index] = bits[index] and another.bits[index].inv()
}
}
/** Performs a logical xor operation over corresponding bits of this and [another] BitSets. The result is saved in this BitSet. */
fun xor(another: BitSet) {
ensureCapacity(another.lastIndex)
for (index in bits.indices) {
bits[index] = bits[index] xor another.bits[index]
}
}
/** Returns true if the specified BitSet has any bits set to true that are also set to true in this BitSet. */
fun intersects(another: BitSet): Boolean =
(0 until minOf(bits.size, another.bits.size)).any { bits[it] and another.bits[it] != 0L }
override fun toString(): String {
val sb = StringBuilder()
var first = true
sb.append('[')
var index = nextSetBit(0)
while (index != -1) {
if (!first) {
sb.append('|')
} else {
first = false
}
sb.append(index)
index = nextSetBit(index + 1)
}
sb.append(']')
return sb.toString()
}
override fun hashCode(): Int {
var x: Long = 1234
for (i in 0..bits.lastIndex) {
x = x xor bits[i] * (i + 1)
}
return (x shr 32 xor x).toInt()
}
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
if (other !is BitSet) {
return false
}
var index = 0
while (index < minOf(bits.size, other.bits.size)) {
if (bits[index] != other.bits[index]) {
return false
}
index++
}
val longestBits = if (bits.size > other.bits.size) bits else other.bits
while (index < longestBits.size) {
if (longestBits[index] != ALL_FALSE) {
return false
}
index++
}
return true
}
}