521 lines
13 KiB
V
521 lines
13 KiB
V
module bitfield
|
|
|
|
/*
|
|
bitfield is a module for
|
|
manipulating arrays of bits, i.e. series of zeroes and ones spread across an
|
|
array of storage units (unsigned 32-bit integers).
|
|
|
|
BitField structure
|
|
------------------
|
|
|
|
Bit arrays are stored in data structures called 'BitField'. The structure is
|
|
'opaque', i.e. its internals are not available to the end user. This module
|
|
provides API (functions and methods) for accessing and modifying bit arrays.
|
|
*/
|
|
|
|
struct BitField {
|
|
mut:
|
|
size int
|
|
//field *u32
|
|
field []u32
|
|
}
|
|
|
|
// helper functions
|
|
const (
|
|
SLOT_SIZE = 32
|
|
)
|
|
|
|
fn bitmask(bitnr int) u32 {
|
|
return u32(u32(1) << u32(bitnr % SLOT_SIZE))
|
|
}
|
|
|
|
fn bitslot(size int) int {
|
|
return size / SLOT_SIZE
|
|
}
|
|
|
|
fn bitget(instance BitField, bitnr int) int {
|
|
return (instance.field[bitslot(bitnr)] >> u32(bitnr % SLOT_SIZE)) & 1
|
|
}
|
|
|
|
fn bitset(instance mut BitField, bitnr int) {
|
|
instance.field[bitslot(bitnr)] = instance.field[bitslot(bitnr)] | bitmask(bitnr)
|
|
}
|
|
|
|
fn bitclear(instance mut BitField, bitnr int) {
|
|
instance.field[bitslot(bitnr)] = instance.field[bitslot(bitnr)] & ~bitmask(bitnr)
|
|
}
|
|
|
|
fn bittoggle(instance mut BitField, bitnr int) {
|
|
instance.field[bitslot(bitnr)] = instance.field[bitslot(bitnr)] ^ bitmask(bitnr)
|
|
}
|
|
/*
|
|
#define BITTEST(a, b) ((a)->field[BITSLOT(b)] & BITMASK(b))
|
|
*/
|
|
|
|
fn min(input1 int, input2 int) int {
|
|
if input1 < input2 {
|
|
return input1
|
|
}
|
|
else {
|
|
return input2
|
|
}
|
|
}
|
|
|
|
fn bitnslots(length int) int {
|
|
return (length - 1) / SLOT_SIZE + 1
|
|
}
|
|
|
|
fn cleartail(instance mut BitField) {
|
|
tail := instance.size % SLOT_SIZE
|
|
if tail != 0 {
|
|
// create a mask for the tail
|
|
mask := u32((1 << tail) - 1)
|
|
// clear the extra bits
|
|
instance.field[bitnslots(instance.size) - 1] = instance.field[bitnslots(instance.size) - 1] & mask
|
|
}
|
|
}
|
|
|
|
// public functions
|
|
|
|
// str2bf() converts a string of characters ('0' and '1') to a bit
|
|
// array. Any character different from '0' is treated as '1'.
|
|
|
|
pub fn str2bf(input string) BitField {
|
|
mut output := new(input.len)
|
|
for i := 0; i < input.len; i++ {
|
|
if input[i] != 48 {
|
|
output.setbit(i)
|
|
}
|
|
}
|
|
return output
|
|
}
|
|
|
|
// string() converts the bit array to a string of characters ('0' and '1') and
|
|
// return the string
|
|
|
|
pub fn (input BitField) string() string {
|
|
mut output := ''
|
|
for i := 0; i < input.size; i++ {
|
|
if input.getbit(i) == 1 {
|
|
output = output + '1'
|
|
}
|
|
else {
|
|
output = output + '0'
|
|
}
|
|
}
|
|
return output
|
|
}
|
|
|
|
//new() creates an empty bit array of capable of storing 'size' bits.
|
|
|
|
pub fn new(size int) BitField {
|
|
output := BitField{
|
|
size: size
|
|
//field: *u32(calloc(bitnslots(size) * SLOT_SIZE / 8))
|
|
field: [u32(0); bitnslots(size)]
|
|
}
|
|
return output
|
|
}
|
|
/*
|
|
pub fn del(instance *BitField) {
|
|
free(instance.field)
|
|
free(instance)
|
|
}
|
|
*/
|
|
|
|
// getbit() returns the value (0 or 1) of bit number 'bit_nr' (count from
|
|
// 0)
|
|
|
|
pub fn (instance BitField) getbit(bitnr int) int {
|
|
if bitnr >= instance.size {return 0}
|
|
return bitget(instance, bitnr)
|
|
}
|
|
|
|
// setbit() set bit number 'bit_nr' to 1 (count from 0)
|
|
|
|
pub fn (instance mut BitField) setbit(bitnr int) {
|
|
if bitnr >= instance.size {return}
|
|
bitset(mut instance, bitnr)
|
|
}
|
|
|
|
// clearbit() clears (sets to zero) bit number 'bit_nr' (count from 0)
|
|
|
|
pub fn (instance mut BitField) clearbit(bitnr int) {
|
|
if bitnr >= instance.size {return}
|
|
bitclear(mut instance, bitnr)
|
|
}
|
|
|
|
// setall() sets all bits in the array to 1
|
|
|
|
pub fn (instance mut BitField) setall() {
|
|
for i := 0; i < bitnslots(instance.size); i++ {
|
|
instance.field[i] = u32(-1)
|
|
}
|
|
cleartail(mut instance)
|
|
}
|
|
|
|
// clearall() clears (sets to zero) all bits in the array
|
|
|
|
pub fn (instance mut BitField) clearall() {
|
|
for i := 0; i < bitnslots(instance.size); i++ {
|
|
instance.field[i] = u32(0)
|
|
}
|
|
}
|
|
|
|
// togglebit() change the value (from 0 to 1 or from 1 to 0) of bit
|
|
// number 'bit_nr'
|
|
|
|
pub fn (instance mut BitField) togglebit(bitnr int) {
|
|
if bitnr >= instance.size {return}
|
|
bittoggle(mut instance, bitnr)
|
|
}
|
|
|
|
// bfand() perform logical AND operation on every pair of bits from 'input1'
|
|
// and 'input2' and return the result as a new array. If inputs differ in size,
|
|
// the tail of the longer one is ignored.
|
|
|
|
pub fn bfand(input1 BitField, input2 BitField) BitField {
|
|
size := min(input1.size, input2.size)
|
|
bitnslots := bitnslots(size)
|
|
mut output := new(size)
|
|
mut i := 0
|
|
for i < bitnslots {
|
|
output.field[i] = input1.field[i] & input2.field[i]
|
|
i++
|
|
}
|
|
cleartail(mut output)
|
|
return output
|
|
}
|
|
|
|
// bfnot() toggle all bits in a bit array and return the result as a new array
|
|
|
|
pub fn bfnot(input BitField) BitField {
|
|
size := input.size
|
|
bitnslots := bitnslots(size)
|
|
mut output := new(size)
|
|
mut i := 0
|
|
for i < bitnslots {
|
|
output.field[i] = ~input.field[i]
|
|
i++
|
|
}
|
|
cleartail(mut output)
|
|
return output
|
|
}
|
|
|
|
// bfor() perform logical OR operation on every pair of bits from 'input1' and
|
|
// 'input2' and return the result as a new array. If inputs differ in size, the
|
|
// tail of the longer one is ignored.
|
|
|
|
pub fn bfor(input1 BitField, input2 BitField) BitField {
|
|
size := min(input1.size, input2.size)
|
|
bitnslots := bitnslots(size)
|
|
mut output := new(size)
|
|
mut i := 0
|
|
for i < bitnslots {
|
|
output.field[i] = input1.field[i] | input2.field[i]
|
|
i++
|
|
}
|
|
cleartail(mut output)
|
|
return output
|
|
}
|
|
|
|
// bfxor(input1 BitField, input2 BitField) perform logical XOR operation on
|
|
// every pair of bits from 'input1' and 'input2' and return the result as a new
|
|
// array. If inputs differ in size, the tail of the longer one is ignored.
|
|
|
|
pub fn bfxor(input1 BitField, input2 BitField) BitField {
|
|
size := min(input1.size, input2.size)
|
|
bitnslots := bitnslots(size)
|
|
mut output := new(size)
|
|
mut i := 0
|
|
for i < bitnslots {
|
|
output.field[i] = input1.field[i] ^ input2.field[i]
|
|
i++
|
|
}
|
|
cleartail(mut output)
|
|
return output
|
|
}
|
|
|
|
// join() concatenates two bit arrays and return the result as a new array.
|
|
|
|
pub fn join(input1 BitField, input2 BitField) BitField {
|
|
output_size := input1.size + input2.size
|
|
mut output := new(output_size)
|
|
// copy the first input to output as is
|
|
for i := 0; i < bitnslots(input1.size); i++ {
|
|
output.field[i] = input1.field[i]
|
|
}
|
|
|
|
// find offset bit and offset slot
|
|
offset_bit := input1.size % SLOT_SIZE
|
|
offset_slot := input1.size / SLOT_SIZE
|
|
|
|
for i := 0; i < bitnslots(input2.size); i++ {
|
|
output.field[i + offset_slot] =
|
|
output.field[i + offset_slot] |
|
|
u32(input2.field[i] << u32(offset_bit))
|
|
}
|
|
|
|
/*
|
|
* If offset_bit is not zero, additional operations are needed.
|
|
* Number of iterations depends on the nr of slots in output. Two
|
|
* options:
|
|
* (a) nr of slots in output is the sum of inputs' slots. In this
|
|
* case, the nr of bits in the last slot of output is less than the
|
|
* nr of bits in second input (i.e. ), OR
|
|
* (b) nr of slots of output is the sum of inputs' slots less one
|
|
* (i.e. less iterations needed). In this case, the nr of bits in
|
|
* the last slot of output is greater than the nr of bits in second
|
|
* input.
|
|
* If offset_bit is zero, no additional copies needed.
|
|
*/
|
|
if (output_size - 1) % SLOT_SIZE < (input2.size - 1) % SLOT_SIZE {
|
|
for i := 0; i < bitnslots(input2.size); i++ {
|
|
output.field[i + offset_slot + 1] =
|
|
output.field[i + offset_slot + 1] |
|
|
u32(input2.field[i] >> u32(SLOT_SIZE - offset_bit))
|
|
}
|
|
} else if (output_size - 1) % SLOT_SIZE > (input2.size - 1) % SLOT_SIZE {
|
|
for i := 0; i < bitnslots(input2.size) - 1; i++ {
|
|
output.field[i + offset_slot + 1] =
|
|
output.field[i + offset_slot + 1] |
|
|
u32(input2.field[i] >> u32(SLOT_SIZE - offset_bit))
|
|
}
|
|
}
|
|
return output
|
|
}
|
|
|
|
// print(instance BitField) send the content of a bit array to stdout as a
|
|
// string of characters ('0' and '1').
|
|
|
|
pub fn print(instance BitField) {
|
|
mut i := 0
|
|
for i < instance.size {
|
|
if instance.getbit(i) == 1 {
|
|
print('1')
|
|
}
|
|
else {
|
|
print('0')
|
|
}
|
|
i++
|
|
}
|
|
}
|
|
|
|
// getsize() returns the number of bits the array can hold
|
|
|
|
pub fn (instance BitField) getsize() int {
|
|
return instance.size
|
|
}
|
|
|
|
// clone() create a copy of a bit array
|
|
|
|
pub fn clone(input BitField) BitField {
|
|
bitnslots := bitnslots(input.size)
|
|
mut output := new(input.size)
|
|
mut i := 0
|
|
for i < bitnslots {
|
|
output.field[i] = input.field[i]
|
|
i++
|
|
}
|
|
return output
|
|
}
|
|
|
|
// cmp() compare two bit arrays bit by bit and return 'true' if they are
|
|
// identical by length and contents and 'false' otherwise.
|
|
|
|
pub fn cmp(input1 BitField, input2 BitField) bool {
|
|
if input1.size != input2.size {return false}
|
|
for i := 0; i < bitnslots(input1.size); i++ {
|
|
if input1.field[i] != input2.field[i] {return false}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// popcount() returns the number of set bits (ones) in the array
|
|
|
|
pub fn (instance BitField) popcount() int {
|
|
size := instance.size
|
|
bitnslots := bitnslots(size)
|
|
tail := size % SLOT_SIZE
|
|
mut count := 0
|
|
for i := 0; i < bitnslots - 1; i++ {
|
|
for j := 0; j < SLOT_SIZE; j++ {
|
|
if u32(instance.field[i] >> u32(j)) & u32(1) == u32(1) {
|
|
count++
|
|
}
|
|
}
|
|
}
|
|
for j := 0; j < tail; j++ {
|
|
if u32(instance.field[bitnslots - 1] >> u32(j)) & u32(1) == u32(1) {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|
|
|
|
// hamming () compute the Hamming distance between two bit arrays.
|
|
|
|
pub fn hamming (input1 BitField, input2 BitField) int {
|
|
input_xored := bfxor(input1, input2)
|
|
return input_xored.popcount()
|
|
}
|
|
|
|
// pos() checks if the array contains a sub-array 'needle' and returns its
|
|
// position if it does, -1 if it does not, and -2 on error.
|
|
|
|
pub fn (haystack BitField) pos(needle BitField) int {
|
|
heystack_size := haystack.size
|
|
needle_size := needle.size
|
|
diff := heystack_size - needle_size
|
|
|
|
// needle longer than haystack; return error code -2
|
|
if diff < 0 {
|
|
return -2
|
|
}
|
|
for i := 0; i <= diff; i++ {
|
|
needle_candidate := haystack.slice(i, needle_size + i)
|
|
if cmp(needle_candidate, needle) {
|
|
// needle matches a sub-array of haystack; return starting position of the sub-array
|
|
return i
|
|
}
|
|
}
|
|
// nothing matched; return -1
|
|
return -1
|
|
}
|
|
|
|
// slice() return a sub-array of bits between 'start_bit_nr' (included) and
|
|
// 'end_bit_nr' (excluded)
|
|
|
|
pub fn (input BitField) slice(_start int, _end int) BitField {
|
|
// boundary checks
|
|
mut start := _start
|
|
mut end := _end
|
|
if end > input.size {
|
|
end = input.size // or panic?
|
|
}
|
|
if start > end {
|
|
start = end // or panic?
|
|
}
|
|
|
|
mut output := new(end - start)
|
|
start_offset := start % SLOT_SIZE
|
|
end_offset := (end - 1) % SLOT_SIZE
|
|
start_slot := start / SLOT_SIZE
|
|
end_slot := (end - 1) / SLOT_SIZE
|
|
output_slots := bitnslots(end - start)
|
|
|
|
if output_slots > 1 {
|
|
if start_offset != 0 {
|
|
for i := 0; i < output_slots - 1; i++ {
|
|
output.field[i] =
|
|
u32(input.field[start_slot + i] >> u32(start_offset))
|
|
output.field[i] = output.field[i] |
|
|
u32(input.field[start_slot + i + 1] <<
|
|
u32(SLOT_SIZE - start_offset))
|
|
}
|
|
}
|
|
else {
|
|
for i := 0; i < output_slots - 1; i++ {
|
|
output.field[i] =
|
|
u32(input.field[start_slot + i])
|
|
}
|
|
}
|
|
}
|
|
|
|
if start_offset > end_offset {
|
|
output.field[(end - start - 1) / SLOT_SIZE] =
|
|
u32(input.field[end_slot - 1] >> u32(start_offset))
|
|
mut mask := u32((1 << (end_offset + 1)) - 1)
|
|
mask = input.field[end_slot] & mask
|
|
mask = u32(mask << u32(SLOT_SIZE - start_offset))
|
|
output.field[(end - start - 1) / SLOT_SIZE] =
|
|
output.field[(end - start - 1) / SLOT_SIZE] | mask
|
|
}
|
|
else if start_offset == 0 {
|
|
mut mask := u32(0)
|
|
if end_offset == SLOT_SIZE - 1 {
|
|
mask = u32(-1)
|
|
}
|
|
else {
|
|
mask = u32(u32(1) << u32(end_offset + 1))
|
|
mask = mask - u32(1)
|
|
}
|
|
output.field[(end - start - 1) / SLOT_SIZE] =
|
|
(input.field[end_slot] & mask)
|
|
}
|
|
else {
|
|
mut mask := u32(((1 << (end_offset - start_offset + 1)) - 1) << start_offset)
|
|
mask = input.field[end_slot] & mask
|
|
mask = u32(mask >> u32(start_offset))
|
|
output.field[(end - start - 1) / SLOT_SIZE] =
|
|
output.field[(end - start - 1) / SLOT_SIZE] | mask
|
|
}
|
|
return output
|
|
}
|
|
|
|
// reverse() reverses the order of bits in the array (swap the first with the
|
|
// last, the second with the last but one and so on)
|
|
|
|
pub fn (instance mut BitField) reverse() BitField {
|
|
size := instance.size
|
|
bitnslots := bitnslots(size)
|
|
mut output := new(size)
|
|
for i:= 0; i < (bitnslots - 1); i++ {
|
|
for j := 0; j < SLOT_SIZE; j++ {
|
|
if u32(instance.field[i] >> u32(j)) & u32(1) == u32(1) {
|
|
bitset(mut output, size - i * SLOT_SIZE - j - 1)
|
|
}
|
|
}
|
|
}
|
|
bits_in_last_input_slot := (size - 1) % SLOT_SIZE + 1
|
|
for j := 0; j < bits_in_last_input_slot; j++ {
|
|
if u32(instance.field[bitnslots - 1] >> u32(j)) & u32(1) == u32(1) {
|
|
bitset(mut output, bits_in_last_input_slot - j - 1)
|
|
}
|
|
}
|
|
return output
|
|
}
|
|
|
|
// resize changes the size of the bit array to 'new_size'
|
|
pub fn (instance mut BitField) resize(new_size int) {
|
|
new_bitnslots := bitnslots(new_size)
|
|
old_size := instance.size
|
|
old_bitnslots := bitnslots(old_size)
|
|
mut field := [u32(0); new_bitnslots]
|
|
for i := 0; i < old_bitnslots && i < new_bitnslots; i++ {
|
|
field[i] = instance.field[i]
|
|
}
|
|
instance.field = field.clone()
|
|
instance.size = new_size
|
|
if size < old_size && size % SLOT_SIZE != 0 {
|
|
cleartail(mut instance)
|
|
}
|
|
}
|
|
|
|
// rotate(offset int) circular-shift the bits by 'offset' positions (move
|
|
// 'offset' bit to 0, 'offset+1' bit to 1, and so on)
|
|
|
|
pub fn (instance BitField) rotate(offset int) BitField {
|
|
/**
|
|
* This function "cuts" the bitfield into two and swaps them.
|
|
* If the offset is positive, the cutting point is counted from the
|
|
* beginning of the bit array, otherwise from the end.
|
|
**/
|
|
size := instance.size
|
|
// removing extra rotations
|
|
|
|
mut offset_internal := offset % size
|
|
if (offset_internal == 0) {
|
|
// nothing to shift
|
|
return instance
|
|
}
|
|
if offset_internal < 0 {
|
|
offset_internal = offset_internal + size
|
|
}
|
|
|
|
first_chunk := instance.slice(0, offset_internal)
|
|
second_chunk := instance.slice(offset_internal, size)
|
|
output := join(second_chunk, first_chunk)
|
|
return output
|
|
}
|