// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
// RUN: %diff "%s.expect" "%t"

// This Dafny program was inspired by Claude Marche's Why3ML program that solves
// Challenge 2 of the COST Verification Competition.  It particular, it uses an
// inductive datatype for the Tree data structure, and it uses a Contains function
// defined on such trees.  This makes the whole program short and sweet, keeps
// proof annotation overhead to a minimum, and--best of all--makes for a convincing
// specification of Max.
// Rustan Leino, 7 Oct 2011

// Remarks:

// A little detail about the implementation of 'Max' below is that the precondition
// 't != Null' means that the 'match' statement does not need to include a case
// for 'Null'.  The correctness of the omission of cases is checked by the program
// verifier.

// About Dafny:
// As always (when it is successful), Dafny verifies that the program does not
// cause any run-time errors (like array index bounds errors), that the program
// terminates, that expressions and functions are well defined, and that all
// specifications are satisfied.  The language prevents type errors by being type
// safe, prevents dangling pointers by not having an "address-of" or "deallocate"
// operation (which is accommodated at run time by a garbage collector), and
// prevents arithmetic overflow errors by using mathematical integers (which
// is accommodated at run time by using BigNum's).  By proving that programs
// terminate, Dafny proves that a program's time usage is finite, which implies
// that the program's space usage is finite too.  However, executing the
// program may fall short of your hopes if you don't have enough time or
// space; that is, the program may run out of space or may fail to terminate in
// your lifetime, because Dafny does not prove that the time or space needed by
// the program matches your execution environment.  The only input fed to
// the Dafny verifier/compiler is the program text below; Dafny then automatically
// verifies and compiles the program (for this program in less than 2 seconds)
// without further human intervention.

datatype Tree = Null | Node(Tree, int, Tree)

function Contains(t: Tree, v: int): bool
{
  match t
  case Null => false
  case Node(left, x, right) => x == v || Contains(left, v) || Contains(right, v)
}

method Max(t: Tree) returns (result: int)
  requires t != Null;
  ensures Contains(t, result) && forall v :: Contains(t, v) ==> v <= result;
{
  match (t) {
    case Node(left, x, right) =>
      result := MaxAux(right, x);
      result := MaxAux(left, result);
  }
}

method MaxAux(t: Tree, acc: int) returns (result: int)
  ensures result == acc || Contains(t, result);
  ensures acc <= result && forall v :: Contains(t, v) ==> v <= result;
{
  match (t) {
    case Null => result := acc;
    case Node(left, x, right) =>
      result := MaxAux(right, if x < acc then acc else x);
      result := MaxAux(left, result);
  }
}
