2

I'm checking if some key shows up only once in an array (where b is the the return value), however the following invariant says it isn't maintained by the loop:

invariant b <==> exists j | 0 <= j < i :: a[j] == key && forall k | 0 <= k < i && j != k :: a[k] != key

The loop continues as follows

  var i := 0;
  b := false;
  var keyCount := 0;
  while i < a.Length
  invariant 0 <= i <= a.Length
  invariant b <==> exists j | 0 <= j < i :: a[j] == key && forall k | 0 <= k < i && j != k :: a[k] != key
  {
    if (a[i] == key)
    { keyCount := keyCount + 1; }
    if (keyCount == 1)
    { b := true; }
    else
    { b := false; }
    i := i + 1;
  }

The logic seems sound to me - is there something I'm missing?

Foxx
  • 78
  • 6

1 Answers1

2

On my setup, Dafny times out when trying to verify your given loop invariant. However, I think you can accomplish what you want by replacing that loop invariant with the stronger invariants:

invariant multiset(a[..i])[key] == keyCount
invariant b <==> (keyCount == 1)

The first states that in the multiset containing the first i elements of a, the count of key is equal to the computed keyCount. The second relates b and keyCount. A full solution is below:

method only_once<a(==)>(a: array<a>, key: a)
{
  var i := 0;
  var b := false;
  var keyCount := 0;
  while i < a.Length
  invariant 0 <= i <= a.Length
  invariant multiset(a[..i])[key] == keyCount
  invariant b <==> (keyCount == 1)
  {
    ghost var preCount := keyCount;
    if (a[i] == key)
    { keyCount := keyCount + 1; }
    if (keyCount == 1)
    { b := true; }
    else
    { b := false; }
    i := i + 1;
  }
  assert a[..a.Length] == a[..];
  assert b <==> multiset(a[..])[key] == 1;
}

I believe the final assert is what you want. I'm not sure why Dafny needs the second-to-last assert a[..a.Length] == a[..], but removing it causes the verification of the last assert to fail.

Daniel Ricketts
  • 447
  • 2
  • 8