Showing equivalence of two bitvectors

Viewed 85

I've been trying to show that two bitvectors are equivalent when all its individual bits are equivalent. In other words, the following statement where a and b are bv64 and BitIsSet is a function extracting the ith bit from the bitvector, and WORD_SIZE is 64.

(a == b) == (forall i | 0 <= i < WORD_SIZE :: (BitIsSet(a, i) == BitIsSet(b, i)))

The first case, where a == b seems trivial for Dafny. So what I'm left to show is that

if a != b {
    assert !(forall i | 0 <= i < WORD_SIZE :: (BitIsSet(a, i) == BitIsSet(b, i)));
}

which basically is showing that there exists an i such that the two bits are different:

    assert exists i | 0 <= i < WORD_SIZE :: (BitIsSet(a, i) != BitIsSet(b, i)) 

Any hints on how to best go about this?

For completeness, BitIsSet basically masks the bit at ith position and compares it against 0.

predicate method {:opaque} BitIsSet(x:bv64, i: nat)
    requires i < WORD_SIZE
{
   (x & ((1 as bv64) << (i as bv7))) != 0
}

The same thing by using sequences of booleans seems to be pretty much trivial, I suspect that there is some kind of axiom being used?

lemma SeqBoolEquiv(a: seq<bool>, b: seq<bool>)
    requires |a| == WORD_SIZE && |b| == WORD_SIZE
    ensures (a == b) <==> (forall i | 0 <= i < WORD_SIZE :: a[i] == b[i])
{
}

Edit: Trying to understand what's going on here: Would the inability to show equivalence for bitvectors (as opposed to sequences) likely be due to a missing axiom at Dafny level, or some possible limitations at Boogie or Z3 showing equivalence of bitvectors there?

1 Answers

I've managed to do the proof, roughly following the proof of a colleague in a different Language. So, Dafny is happy to show that

(a & 0xffff_ffff_ffff_ffff) == (b & 0xffff_ffff_ffff_ffff) <==> a == b

We can use this and do induction over the bitmask, converting our initial proof into:

((a & Bitmask(N)) == (b & Bitmask(N)) == (forall i | 0 <= i < N :: (BitIsSet(a, i) == BitIsSet(b, i)))

then do induction over N with base case N == 0 where Bitmask(0) == 0, and show the step case reasoning about the additional bit.

Finally, we can use the induction proof to get the result for N == WORD_SIZE which gives us the original lemma as (a & Bitmask(WORD_SIZE)) == a.

EDIT: The BitMask definition. Does rely on overflow to get BitMask(64) working, i.e., it shifts the 1 out of the bv64, then 0 - 1 == 0xff..ff. Dafny seems happy with it, and I can show that BitMask(WORD_SIZE) == 0xff..ff

    function method {:opaque} BitMask(i: uint64) : bv64
        requires i <= WORD_SIZE
    {
        ((1 as bv64) << (i as bv7)) - 1
    }
Related