-- Copyright 1993-1998, by the Cecil Project -- Department of Computer Science and Engineering, University of Washington -- See the LICENSE file for license information. (--DOC Bit vectors are a dense representation of a mutable indexed collection of zeros and ones. A new bit vector of zeros can be created with `new_bit_vector'. A bit vector can be resized in place; if expanded, then the new positions are filled in with zeros. --) -- Some really offensive stuff going on here....lots of things written as -- primitives that really wouldn't need to be if we could pass untagged -- small ints as parameters at the Cecil level. -- All code assumes that bit vectors are kept in a canonicalized form in which -- any 'garbage' bits trailing the last data bit in the last word are set to 0. module BitVector { template object bit_vector isa m_indexed[int]; -- there's no Cecil type for an untaggedInt, so we have to use none. -- 'bits' can only sensibly be accessed from inside an RTL primitive. private field bits(@:bit_vector):none; -- name this field 'len' because 'length' is now a reserved word in RTL private put var field len(@:bit_vector):int := 0; method length(t@:bit_vector):int { t.len } -- Set to 0 any bits past the last "real" bit in the last word of bit vector private method canonicalize_final_word(t@:bit_vector):void { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP box_blen := t.len@bit_vector; decl wordInt blen := unbox_int box_blen; decl wordInt valid_bits := blen &_int bit_pos_mask; if valid_bits =_int_log 0 goto done; decl OOP bits := t.bits@bit_vector; decl wordInt word_num := blen >>_int log_bits_per_machine_word; decl wordInt word := bits[word_num] wordInt; decl wordInt extra_bits := bits_per_machine_word -_int valid_bits; decl wordInt mask := all_ones_word; mask := mask >>_logical_int extra_bits; word := word &_int mask; bits[word_num] wordInt := word; label done; return void;" } -- Bit vector creation/cloning private method new_backing_bit_vector(num_bits:int):none { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl wordInt blen := unbox_int num_bits; decl wordInt len := blen +_int bit_pos_mask; len := len >>_int log_bits_per_machine_word; decl OOP vec := new_m_word_vector len; if len =_int_log 0 goto exit; decl wordInt i := 0; label loop; vec[i] wordInt := all_zeros_word; i := i +_int 1; if i <_int_log len goto loop; label exit; return vec; " } private method copy_backing_vector(t@:bit_vector):none { prim rtl: " decl OOP bits := t.bits@bit_vector; decl wordInt len := num_elems_int bits; decl OOP vec := new_m_word_vector len; if len =_int_log 0 goto exit; decl wordInt i := 0; label loop; decl wordInt tmp := bits[i] wordInt; vec[i] wordInt := tmp; i := i +_int 1; if i <_int_log len goto loop; label exit; return vec;" } private method new_bit_vector(sz:int, bv:none):bit_vector { concrete object isa bit_vector { bits := bv, len := sz } } method new_bit_vector(sz:int):bit_vector { new_bit_vector(sz, new_backing_bit_vector(sz)) } method copy(t@:bit_vector):bit_vector { concrete object isa bit_vector { bits := t.copy_backing_vector, len := t.len } } method resize(t@:bit_vector, new_bits:int):void { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl wordInt nblen := unbox_int new_bits; decl wordInt nlen := nblen +_int bit_pos_mask; nlen := nlen >>_int log_bits_per_machine_word; decl OOP obits := t.bits@bit_vector; decl wordInt olen := num_elems_int obits; decl OOP nbits := new_m_word_vector nlen; decl wordInt min_len := olen; if olen <_int_log nlen goto cont; min_len := nlen; label cont; decl wordInt i := 0; if min_len =_int_log 0 goto pre_loop2; label loop1; decl wordInt w := obits[i] wordInt; nbits[i] wordInt := w; i := i +_int 1; if i <_int_log min_len goto loop1; label pre_loop2; if min_len =_int_log nlen goto done; label loop2; nbits[i] wordInt := all_zeros_word; i := i +_int 1; if i <_int_log nlen goto loop2; label done; t.len@bit_vector := new_bits; t.bits@bit_vector := nbits; return void;" } method =(l@:bit_vector, r@:bit_vector):bool { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; if l =_int_log r goto succ; decl OOP lblen := l.len@bit_vector; decl OOP rblen := r.len@bit_vector; if lblen !=_int_log rblen goto fail; decl OOP lbits := l.bits@bit_vector; decl OOP rbits := r.bits@bit_vector; decl wordInt len := num_elems_int lbits; if len =_int_log 0 goto succ; decl wordInt i := 0; label loop; decl wordInt w1 := lbits[i] wordInt; decl wordInt w2 := rbits[i] wordInt; if w1 !=_int_log w2 goto fail; i := i+_int 1; if i <_int_log len goto loop; label succ; return true; label fail; return false;" } -- bit-set support function; works specially to ignore explicit trailing zeros method =_or_zero(l@:bit_vector, r@:bit_vector):bool { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP lbits := l.bits@bit_vector; decl OOP rbits := r.bits@bit_vector; decl OOP llen := num_elems_int lbits; decl OOP rlen := num_elems_int rbits; decl OOP short_bits; decl OOP long_bits; decl wordInt short_len; decl wordInt long_len; if llen <_int_log rlen goto l1; short_bits := rbits; short_len := rlen; long_bits := lbits; long_len := llen; goto cont; label l1; short_bits := lbits; short_len := llen; long_bits := rbits; long_len := rlen; label cont; decl wordInt i := 0; if short_len =_int_log 0 goto pre_loop2; label loop1; decl wordInt w1 := long_bits[i] wordInt; decl wordInt w2 := short_bits[i] wordInt; if w1 !=_int_log w2 goto fail; i := i +_int 1; if i <_int_log short_len goto loop1; label pre_loop2; if long_len =_int_log short_len goto succ; label loop2; decl wordInt nw := long_bits[i] wordInt; if nw !=_int_log all_zeros_word goto fail; i := i +_int 1; if i <_int_log long_len goto loop2; label succ; return true; label fail; return false;" } --DOC Bit vectors can be or'd, and'd, xor'd, subtracted, and negated, all --DOC bitwise, to compute new bit vectors from old. -- or does not need last word canonicalized method bit_or(l@:bit_vector, r@:bit_vector):bit_vector { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP lbits := l.bits@bit_vector; decl OOP rbits := r.bits@bit_vector; decl OOP llen := num_elems_int lbits; decl OOP rlen := num_elems_int rbits; decl OOP short_bits; decl OOP long_bits; decl OOP long_blen; decl wordInt short_len; decl wordInt long_len; if llen <_int_log rlen goto l1; short_bits := rbits; short_len := rlen; long_bits := lbits; long_len := llen; long_blen := l.len@bit_vector; goto cont; label l1; short_bits := lbits; short_len := llen; long_bits := rbits; long_len := rlen; long_blen := r.len@bit_vector; label cont; decl OOP nbits := new_m_word_vector long_len; decl wordInt i := 0; if short_len =_int_log 0 goto pre_loop2; label loop1; decl wordInt w1 := long_bits[i] wordInt; decl wordInt w2 := short_bits[i] wordInt; decl wordInt nw := w1 |_int w2; nbits[i] wordInt := nw; i := i +_int 1; if i <_int_log short_len goto loop1; label pre_loop2; if long_len =_int_log short_len goto done; label loop2; decl wordInt nw := long_bits[i] wordInt; nbits[i] wordInt := nw; i := i +_int 1; if i <_int_log long_len goto loop2; label done; decl OOP nbv := send new_bit_vector(long_blen, nbits); return nbv;" } -- and does not need last word canonicalized method bit_and(l@:bit_vector, r@:bit_vector):bit_vector { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP lbits := l.bits@bit_vector; decl OOP rbits := r.bits@bit_vector; decl OOP llen := num_elems_int lbits; decl OOP rlen := num_elems_int rbits; decl OOP short_bits; decl OOP long_bits; decl OOP long_blen; decl wordInt short_len; decl wordInt long_len; if llen <_int_log rlen goto l1; short_bits := rbits; short_len := rlen; long_bits := lbits; long_len := llen; long_blen := l.len@bit_vector; goto cont; label l1; short_bits := lbits; short_len := llen; long_bits := rbits; long_len := rlen; long_blen := r.len@bit_vector; label cont; decl OOP nbits := new_m_word_vector long_len; decl wordInt i := 0; if short_len =_int_log 0 goto pre_loop2; label loop1; decl wordInt w1 := long_bits[i] wordInt; decl wordInt w2 := short_bits[i] wordInt; decl wordInt nw := w1 &_int w2; nbits[i] wordInt := nw; i := i +_int 1; if i <_int_log short_len goto loop1; label pre_loop2; if long_len =_int_log short_len goto done; label loop2; nbits[i] wordInt := all_zeros_word; i := i +_int 1; if i <_int_log long_len goto loop2; label done; decl OOP nbv := send new_bit_vector(long_blen, nbits); return nbv;" } -- xor does not need last word canonicalized method bit_xor(l@:bit_vector, r@:bit_vector):bit_vector { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP lbits := l.bits@bit_vector; decl OOP rbits := r.bits@bit_vector; decl OOP llen := num_elems_int lbits; decl OOP rlen := num_elems_int rbits; decl OOP short_bits; decl OOP long_bits; decl OOP long_blen; decl wordInt short_len; decl wordInt long_len; if llen <_int_log rlen goto l1; short_bits := rbits; short_len := rlen; long_bits := lbits; long_len := llen; long_blen := l.len@bit_vector; goto cont; label l1; short_bits := lbits; short_len := llen; long_bits := rbits; long_len := rlen; long_blen := r.len@bit_vector; label cont; decl OOP nbits := new_m_word_vector long_len; decl wordInt i := 0; if short_len =_int_log 0 goto pre_loop2; label loop1; decl wordInt w1 := long_bits[i] wordInt; decl wordInt w2 := short_bits[i] wordInt; decl wordInt nw := w1 ^_int w2; nbits[i] wordInt := nw; i := i +_int 1; if i <_int_log short_len goto loop1; label pre_loop2; if long_len =_int_log short_len goto done; label loop2; decl wordInt nw := long_bits[i] wordInt; nbits[i] wordInt := nw; i := i +_int 1; if i <_int_log long_len goto loop2; label done; decl OOP nbv := send new_bit_vector(long_blen, nbits); return nbv;" } method bit_xnor(l@:bit_vector, r@:bit_vector):bit_vector { -- could be written in-line as a primitive, if it's important.... l _bit_xor _bit_not r } -- must canonicalize final word of result method bit_not(l@:bit_vector):bit_vector { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP lbits := l.bits@bit_vector; decl wordInt llen := num_elems_int lbits; decl OOP nbits := new_m_word_vector llen; if llen =_int_log 0 goto done; decl wordInt i := 0; label loop; decl wordInt lword := lbits[i] wordInt; decl wordInt nword := ~_int lword; nbits[i] wordInt := nword; i := i+_int 1; if i <_int_log llen goto loop; label done; decl OOP lblen := l.len@bit_vector; decl OOP nbv := send new_bit_vector(lblen, nbits); decl OOP junk := send canonicalize_final_word(nbv); return nbv;" } -- similar to bit_and(l, bit_not(r)), if r is padded out to the length of l method bit_difference(l@:bit_vector, r@:bit_vector):bit_vector { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP lbits := l.bits@bit_vector; decl OOP rbits := r.bits@bit_vector; decl wordInt llen := num_elems_int lbits; decl wordInt rlen := num_elems_int rbits; decl OOP nbits := new_m_word_vector llen; if llen =_int_log 0 goto done; decl wordInt i := 0; label loop; decl wordInt lword := lbits[i] wordInt; decl wordInt rword := all_ones_word; if i >=_int_log rlen goto cont; rword := rbits[i] wordInt; rword := ~_int rword; label cont; decl wordInt nword := lword &_int rword; nbits[i] wordInt := nword; i := i +_int 1; if i <_int_log llen goto loop; label done; decl OOP lblen := l.len@bit_vector; decl wordInt ans := send new_bit_vector(lblen, nbits); return ans;" } -- includes_all and is_disjoint are implemented here to avoid allocating -- a whole new bit vector to answer a simple boolean query -- Returns true iff l includes all the bits that are set in r method includes_all(l@:bit_vector, r@:bit_vector):bool { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP rbits := r.bits@bit_vector; decl OOP lbits := l.bits@bit_vector; decl wordInt rlen := num_elems_int rbits; decl wordInt llen := num_elems_int lbits; if rlen =_int_log 0 goto succ; decl wordInt i := 0; label loop; decl wordInt rword := rbits[i] wordInt; decl wordInt lword := all_zeros_word; if i >=_int_log llen goto cont; lword := lbits[i] wordInt; label cont; decl wordInt test := lword &_int rword; if test !=_int_log rword goto fail; i := i +_int 1; if i <_int_log rlen goto loop; label succ; return true; label fail; return false;" } --DOC A bit vector can be mutated to be all zeros (`clear_all_bits') --DOC or all ones (`set_all_bits') or tested for those conditions. --DOC The `is_disjoint' method returns true if its arguments share no --DOC set bits; is_disjoint(a,b) is equivalent to `is_all_zeros(bit_and(a,b))'. -- Returns true iff l and r have no set bits in common method is_disjoint(l@:bit_vector, r@:bit_vector):bool { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP lbits := l.bits@bit_vector; decl OOP rbits := r.bits@bit_vector; decl wordInt llen := num_elems_int lbits; decl wordInt rlen := num_elems_int rbits; decl wordInt len := llen; if rlen >_int_log llen goto cont; len := rlen; label cont; decl wordInt i := 0; if len =_int_log 0 goto succ; label loop; decl wordInt lword := lbits[i] wordInt; decl wordInt rword := rbits[i] wordInt; decl wordInt zp := lword &_int rword; if zp !=_int_log all_zeros_word goto fail; i := i +_int 1; if i <_int_log len goto loop; label succ; return true; label fail; return false;" } method fetch(v@:bit_vector, bit:int, if_absent:&():int):int { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP box_blen := v.len@bit_vector; decl wordInt idx := unbox_int bit; decl wordInt blen := unbox_int box_blen; if idx <_unsigned_log blen goto bounds_OK; decl OOP res := send eval(if_absent); return res; label bounds_OK; decl OOP bits := v.bits@bit_vector; decl wordInt wnum := idx >>_int log_bits_per_machine_word; decl wordInt word := bits[wnum] wordInt; decl wordInt bitpos := idx &_int bit_pos_mask; decl wordInt mask := 1 <<_int bitpos; decl wordInt bitp := word &_int mask; if bitp =_int_log 0 goto l; decl wordInt res1 := box_int 1; return res1; label l; decl wordInt res0 := box_int 0; return res0;" } method store(v@:bit_vector, bit:int, value:int, if_absent:&():void):void { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP box_blen := v.len@bit_vector; decl wordInt idx := unbox_int bit; decl wordInt blen := unbox_int box_blen; if idx <_unsigned_log blen goto bounds_OK; decl OOP res := send eval(if_absent); return res; label bounds_OK; decl OOP bits := v.bits@bit_vector; decl wordInt wnum := idx >>_int log_bits_per_machine_word; decl wordInt word := bits[wnum] wordInt; decl wordInt bitpos := idx &_int bit_pos_mask; decl wordInt mask := 1 <<_int bitpos; if value =_int_log 0 goto clear; word := word |_int mask; bits[wnum] wordInt := word; return void; label clear; mask := ~_int mask; word := word &_int mask; bits[wnum] wordInt := word; return void;" } method set_all_bits(v@:bit_vector):void { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP bits := v.bits@bit_vector; decl wordInt len := num_elems_int bits; decl wordInt i := 0; if len =_int_log 0 goto done; label loop; bits[i] wordInt := all_ones_word; i := i +_int 1; if i <_int_log len goto loop; label done; decl OOP junk := send canonicalize_final_word(v); return void; " } method clear_all_bits(v@:bit_vector):void { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP bits := v.bits@bit_vector; decl wordInt len := num_elems_int bits; decl wordInt i := 0; if len =_int_log 0 goto done; label loop; bits[i] wordInt := all_zeros_word; i := i +_int 1; if i <_int_log len goto loop; label done; return void; " } method is_all_zeros(v@:bit_vector):bool { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP bits := v.bits@bit_vector; decl wordInt len := num_elems_int bits; decl wordInt i := 0; if len =_int_log 0 goto succ; label loop; decl wordInt word := bits[i] wordInt; if word !=_int_log all_zeros_word goto fail; i := i +_int 1; if i <_int_log len goto loop; label succ; return true; label fail; return false;" } -- a little bit trickier, since we have to disregard trailing 0's on last word method is_all_ones(v@:bit_vector):bool { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP bits := v.bits@bit_vector; decl wordInt len := num_elems_int bits; decl wordInt i := 0; if len =_int_log 0 goto succ; label loop; decl wordInt word := bits[i] wordInt; if word =_int_log all_ones_word goto cont_loop; decl wordInt lasti := len -_int 1; if i !=_int_log lasti goto fail; decl OOP box_blen := v.len@bit_vector; decl wordInt blen := unbox_int box_blen; decl wordInt valid_bits := blen &_int bit_pos_mask; decl wordInt mask := all_ones_word; mask := mask >>_logical_int valid_bits; mask := mask <<_int valid_bits; word := word |_int mask; if word !=_int_log all_ones_word goto fail; goto succ; label cont_loop; i := i +_int 1; if i <_int_log len goto loop; label succ; return true; label fail; return false;" } --DOC The `do_ones' control structure iterates over all the set --DOC positions in the bit vector; this operation runs faster than a --DOC regular `do' loop containing a test in each iteration. -- Evalutes the argument closure for each position in the bit vector that is -- non-zero. Does a quick test to skip words that are 0. method do_ones(v@:bit_vector, cl:&(int):void):void { prim rtl: " decl wordInt bit_pos_mask := bits_per_machine_word -_int 1; decl wordInt all_ones_word := -1; decl wordInt all_zeros_word := 0; decl OOP box_bits_per_machine_word := box_int bits_per_machine_word; decl OOP box_bit_pos := box_int 0; decl OOP box_inc := box_int 1; decl OOP bits := v.bits@bit_vector; decl wordInt len := num_elems_int bits; decl wordInt i := 0; label word_loop; decl wordInt word := bits[i] wordInt; if word =_int_log all_zeros_word goto cont_word_loop; decl OOP j := box_int 0; decl wordInt mask := 1; label bit_loop; decl wordInt bitp := word &_int mask; if bitp =_int_log 0 goto cont_bit_loop; decl OOP arg_bit_pos := box_bit_pos +_int j; decl OOP junk := send eval(cl, arg_bit_pos); label cont_bit_loop; j := j +_int box_inc; mask := mask <<_int 1; if j <_int_log box_bits_per_machine_word goto bit_loop; label cont_word_loop; box_bit_pos := box_bit_pos +_int box_bits_per_machine_word; i := i +_int 1; if i <_int_log len goto word_loop; return void;" } method collection_name(@:bit_vector):string { "bit_vector" } method elem_separator(@:bit_vector):string { "" } }; -- end module BitVector