diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 0c675da553a..4c70390b54f 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -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" diff --git a/backend.native/tests/runtime/collections/BitSet.kt b/backend.native/tests/runtime/collections/BitSet.kt new file mode 100644 index 00000000000..ded33f65d2a --- /dev/null +++ b/backend.native/tests/runtime/collections/BitSet.kt @@ -0,0 +1,417 @@ +fun assertContainsOnly(bitSet: BitSet, trueBits: Set, 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, 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 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 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) { + testConstructor() + testSet() + testFlip() + testNextBit() + testLogic() + testEqualsHashCode() +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/BitSet.kt b/runtime/src/main/kotlin/kotlin/BitSet.kt new file mode 100644 index 00000000000..6c3d4ba7482 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/BitSet.kt @@ -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 + 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 + } +}