blob: 4b6a5a1a76ab12f627f51bc2589925e932c1483e [file] [edit]
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:cfg/utils/bit_vector.dart';
import 'package:test/test.dart';
void main() {
test('size 0', () {
final bv1 = BitVector(0);
final bv2 = BitVector(0);
final bv3 = BitVector(0);
expect(bv1.elements, isEmpty);
bv1.intersect(bv2);
expect(bv1.elements, isEmpty);
expect(bv1.addAll(bv2), isFalse);
expect(bv1.elements, isEmpty);
expect(bv1.addSubtraction(bv2, bv3), isFalse);
expect(bv1.elements, isEmpty);
expect(bv1.addIntersection(bv2, bv3), isFalse);
expect(bv1.elements, isEmpty);
});
test('indexing add remove', () {
for (var n in [20, 63, 64, 65, 200, 2000]) {
final mid = n ~/ 2;
final bv = BitVector(n);
expect(bv[0], isFalse);
expect(bv[mid], isFalse);
expect(bv[n - 1], isFalse);
bv.add(0);
bv.add(mid);
bv.add(n - 1);
expect(bv[0], isTrue);
expect(bv[1], isFalse);
expect(bv[mid - 1], isFalse);
expect(bv[mid], isTrue);
expect(bv[n - 2], isFalse);
expect(bv[n - 1], isTrue);
expect(bv.elements.toList(), equals([0, mid, n - 1]));
bv.remove(0);
bv.remove(1);
bv.remove(n - 1);
expect(bv[0], isFalse);
expect(bv[1], isFalse);
expect(bv[mid - 1], isFalse);
expect(bv[mid], isTrue);
expect(bv[n - 2], isFalse);
expect(bv[n - 1], isFalse);
expect(bv.elements.toList(), equals([mid]));
bv[0] = true;
bv[1] = false;
bv[mid - 1] = true;
bv[n - 2] = true;
expect(bv[0], isTrue);
expect(bv[1], isFalse);
expect(bv[mid - 1], isTrue);
expect(bv[mid], isTrue);
expect(bv[n - 2], isTrue);
expect(bv[n - 1], isFalse);
expect(bv.elements.toList(), equals([0, mid - 1, mid, n - 2]));
}
});
test('bulk operations', () {
for (var n in [20, 63, 64, 65, 200, 2000]) {
final bv1 = BitVector(n);
final bv2 = BitVector(n);
final bv3 = BitVector(n);
for (var i = 0; i < n; i += 3) {
bv2[i] = true;
}
for (var i = 0; i < n; i += 5) {
bv3[i] = true;
}
expect(bv1.addAll(bv2), isTrue);
expect(bv1.addAll(bv2), isFalse);
bv1.intersect(bv3);
for (var i = 0; i < n; ++i) {
expect(bv1[i], equals(i % 15 == 0));
expect(bv2[i], equals(i % 3 == 0));
expect(bv3[i], equals(i % 5 == 0));
}
expect(bv2.addAll(bv1), isFalse);
expect(bv3.addAll(bv1), isFalse);
for (var i = 0; i < n; ++i) {
expect(bv1[i], equals(i % 15 == 0));
expect(bv2[i], equals(i % 3 == 0));
expect(bv3[i], equals(i % 5 == 0));
}
expect(bv2.addSubtraction(bv3, bv1), isTrue);
for (var i = 0; i < n; ++i) {
expect(bv1[i], equals(i % 15 == 0));
expect(bv2[i], equals(i % 3 == 0 || i % 5 == 0));
expect(bv3[i], equals(i % 5 == 0));
}
expect(bv3.addSubtraction(bv1, bv2), isFalse);
expect(bv3.addIntersection(bv1, bv2), isFalse);
expect(bv1.addIntersection(bv2, bv3), isTrue);
for (var i = 0; i < n; ++i) {
expect(bv1[i], equals(i % 5 == 0));
expect(bv2[i], equals(i % 3 == 0 || i % 5 == 0));
expect(bv3[i], equals(i % 5 == 0));
}
bv3.clear();
for (var i = 0; i < n; ++i) {
expect(bv3[i], isFalse);
}
var k = 0;
for (int bit in bv1.elements) {
expect(bit, equals(k * 5));
++k;
}
expect(k, equals(1 + (n - 1) ~/ 5));
}
});
test('expand', () {
for (final initialSize in [0, 20, 63, 64, 65, 120]) {
final bv = BitVector(initialSize);
final initialCapacity = bv.capacity;
for (var i = 0; i < initialCapacity; ++i) {
if (i % 3 == 0) {
bv[i] = true;
}
}
final expanded = bv.expand(initialCapacity + 100);
expect(expanded.capacity, greaterThanOrEqualTo(initialCapacity + 100));
// Existing bits should be preserved.
for (var i = 0; i < initialCapacity; ++i) {
expect(expanded[i], equals(i % 3 == 0));
}
// Newly added capacity should be initialized to zero/false.
for (var i = initialCapacity; i < expanded.capacity; ++i) {
expect(expanded[i], isFalse);
}
}
});
test('setRange', () {
void fillPattern(BitVector bv, int seed) {
for (var i = 0; i < bv.capacity; ++i) {
bv[i] = ((i * 17 + seed) & 3) == 0;
}
}
void referenceSetRange(
BitVector dst,
int start,
int end,
BitVector src, [
int skipCount = 0,
]) {
for (var k = 0; k < end - start; ++k) {
dst[start + k] = src[skipCount + k];
}
}
void checkSetRange(
int dstSize,
int srcSize,
int start,
int end,
int skipCount,
) {
final actual = BitVector(dstSize);
final expected = BitVector(dstSize);
final src = BitVector(srcSize);
fillPattern(actual, 1);
fillPattern(expected, 1);
fillPattern(src, 5);
actual.setRange(start, end, src, skipCount);
referenceSetRange(expected, start, end, src, skipCount);
for (var i = 0; i < actual.capacity; ++i) {
expect(
actual[i],
equals(expected[i]),
reason:
'Mismatch at bit $i for setRange(start=$start, end=$end, '
'skipCount=$skipCount, dstCapacity=${actual.capacity}, '
'srcCapacity=${src.capacity})',
);
}
}
// Empty range.
checkSetRange(100, 100, 10, 10, 5);
// Within a single 64-bit word.
checkSetRange(100, 100, 5, 35, 10);
checkSetRange(100, 100, 0, 60, 2);
// Spanning across a word boundary without whole inner words.
checkSetRange(128, 128, 50, 75, 10);
checkSetRange(128, 128, 60, 68, 0);
// Word-aligned start and end (shiftLo == 0).
checkSetRange(256, 256, 0, 64, 0);
checkSetRange(256, 256, 64, 192, 64);
checkSetRange(256, 256, 64, 192, 0);
// Aligned relative skipCount ((start & 63) == (skipCount & 63)) -> shiftLo == 0.
checkSetRange(300, 300, 10, 202, 74);
// Unaligned relative skipCount -> shiftLo != 0.
checkSetRange(300, 300, 10, 202, 15);
checkSetRange(300, 300, 0, 150, 33);
checkSetRange(300, 300, 63, 195, 1);
// Exhaustive small/medium range combinations across word boundaries.
for (final start in [0, 1, 31, 63, 64, 65, 100]) {
for (final length in [0, 1, 30, 63, 64, 65, 127, 128, 130]) {
for (final skipCount in [0, 1, 33, 63, 64, 65]) {
final end = start + length;
final dstSize = end + 70;
final srcSize = skipCount + length + 70;
checkSetRange(dstSize, srcSize, start, end, skipCount);
}
}
}
});
}