stdlib: Add BitSet implementation.
This commit is contained in:
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user