6.S0576.S057: Verified Software Engineering

Class 1

class01.dfy
dfy
// MIT 6.S057 Fall 2026, Class 1

// Some fancier examples to show off the power and basic ideas of Dafny

// Three ways to write one simple function:
// look up whether an integer is present in an "array" of integers,
// returning the position if so.
// Note that earlier classes have harped on the importance of comments
// to explain the purposes of functions, but here we write that information
// out formally instead!

// This version uses two 'ensures' clauses with different implications.
method IndexOf_recursive_two_implications(s: seq<int>, e: int) returns (res: int)
  requires 0 < |s|
  ensures res < 0 ==> e !in s
  ensures 0 <= res ==> res < |s| && s[res] == e
{
  if s[0] == e {
   res := 0;
  } else {
    if |s| == 1 {
      res := -1;
    } else {
      res := IndexOf_recursive_two_implications(s[1..], e);
      if res != -1 {
        res := res + 1;
      }
    }
  }
}

// This version instead uses one 'ensures' with an 'or'.
// Can you spot a practical difference between these two specificatons?
method IndexOf_recursive_disjunction(s: seq<int>, e: int) returns (res: int)
  requires 0 < |s|
  ensures (res == -1 && e !in s) || (0 <= res < |s| && s[res] == e)
{
  if s[0] == e {
   res := 0;
  } else {
    if |s| == 1 {
      res := -1;
    } else {
      res := IndexOf_recursive_disjunction(s[1..], e);
      if res != -1 {
        res := res + 1;
      }
    }
  }
}

// We can also write this routine with a loop, though it forces us to
// add a loop invariant.
method IndexOf_iterative(s: seq<int>, e: int) returns (res: int)
  requires 0 < |s|
  ensures res < 0 ==> e !in s
  ensures 0 <= res ==> res < |s| && s[res] == e
{
  for i := 0 to |s|
    invariant e !in s[..i] {
    if s[i] == e {
      return i;
    }
  }

  return -1;
}

// Here is another classic: insertion sort!
// Note that 'seq' above is for an immutable "array," whereas here we go mutable.

method InsertionSort(a: array<int>)
  modifies a
  ensures multiset(a[..]) == old(multiset(a[..]))
  ensures forall n, m :: 0 <= n < m < a.Length ==> a[n] <= a[m]
{
  for i := 0 to a.Length
    invariant multiset(a[..]) == old(multiset(a[..]))
    invariant forall n, m :: 0 <= n < m < i ==> a[n] <= a[m]
    invariant forall n, m :: 0 <= n < i <= m < a.Length ==> a[n] <= a[m]
  {
    var lowest := i;

    for j := i+1 to a.Length
      invariant i <= lowest < j
      invariant forall k :: i <= k < j ==> a[lowest] <= a[k]
      // invariant multiset(a[..]) == old(multiset(a[..]))
      // invariant forall n, m :: 0 <= n < m < i ==> a[n] <= a[m]
      // invariant forall n, m :: 0 <= n < i && i <= m < a.Length ==> a[n] <= a[m]
      // One might have expected the above three invariants to be required,
      // but Dafny is smart about noticing that this loop does not *modify*
      // the array 'a', hence prior knowledge about 'a' is preserved.
    {
      if a[j] < a[lowest] {
        lowest := j;
      }
    }

    var old_i := a[i];
    a[i] := a[lowest];
    a[lowest] := old_i;
  }
}

// Now let's go back through the basics.
// We include multiple numbered versions, to capture different edits made in class.

method Triple(x: int) returns (r: int) {
  var y := 2 * x;
  r := x + y;
  assert r == 3 * x;
}

method Triple2(x: int) returns (r: int) {
  var y := 2 * x;
  r := x + y;
  assert r == 10 * x;
  assert r < 5; // Very surprising this one is accepted by Dafny!  Do you see why?
  assert false;
}

method Triple3(x: int) returns (r: int) {
  if x == 0 {
    r := 0;
  } else {
    var y := 2 * x;
    r := x + y;
  }
  assert r == 3 * x;
}

method Caller() {
  var t := Triple(18);
  assert t < 100;
}

method Triple4(x: int) returns (r: int)
  ensures r == 3 * x
{
  var y := 2 * x;
  r := x + y;
}

method Caller2() {
  var t := Triple4(18);
  assert t < 100;
}

method Triple5(x: int) returns (r: int)
  requires x % 2 == 0
  ensures r == 3 * x
{
  var y := 2 * x;
  r := x + y;
}

method Index(n: int) returns (i: int)
  requires 1 <= n
  ensures 0 <= i < n
{
  i := n / 2;
}

method Index2(n: int) returns (i: int)
  requires 1 <= n
  ensures 0 <= i < n
{
  i := 0;
}

method Doesn'tWork() {
  var x := Index(50);
  var y := Index(50);
  assert x == y;
}

method Min0(x: int, y: int) returns (m: int)
  ensures m <= x && m <= y

method Min(x: int, y: int) returns (m: int)
  ensures m <= x && m <= y
  ensures m == x || m == y

function Average(a: int, b: int): int {
  (a + b) / 2
}

method Triple'(x: int) returns (r: int)
  ensures Average(r, 3 * x) == 3 * x

function Average2(a: int, b: int): int
  requires 0 <= a && 0 <= b
{
  (a + b) / 2
}

method Triple6(x: int) returns (r: int)
  ensures r == 3 * x
{
  if 0 <= x {
    r := Average(2 * x, 4 * x);
  } else {
    r := -Average(-2 * x, -4 * x);
  }
}

method IllegalAssignment() returns (y: int) {
  ghost var x := 10;
  y := 2 * x;
}

ghost method DoubleQuadruple(x: int) returns (a: int, b: int)
  ensures a == 2 && b == 4 * x
{
  a := 2 * x;
  b := 2 * a;
}

method Triple7(x: int) returns (r: int)
  ensures r == 3 * x
{
  var y := 2 * x;
  r := x + y;
  ghost var a, b := DoubleQuadruple(x);
  assert a <= r <= b || b <= r <= a;
}
Copyright 6.S057 course staff.