class Node {
  var value:int;
  var next:Node;

  predicate isList {
    acc(value) && acc(next) && (next != null ==> next.isList)  
  }
  
  function getValue():int
    requires isList
  {
    unfolding isList in value 
  }
  
}

class List {
  var head:Node
  var size:int
  
  predicate listOK {
    acc(head) && acc(size) && (head != null ==> head.isList)  
  }
  
  method emptyList() returns (res:List)
    ensures res != null && res.listOK
  {
    res :=  new List { head := null, size := 0 }
    fold res.listOK;
  }
  
  method insert(i:int)
    requires listOK
    ensures listOK
  {
    unfold listOK
    call head := makeNode(i, head);
    assert acc(head) && acc(size) && (head != null ==> head.isList)
    fold listOK
  }
  
  method makeNode(v:int, n:Node) returns (res:Node)
    requires n != null ==> n.isList
    ensures res != null
    ensures res.isList
  {
    res :=  new Node { value := v, next := n }
    fold res.isList;
  }
  
  method main() {
      call list1 := emptyList();
      call list1.insert(5);
      call list1.insert(6);
      //assert list1.length() == 2;
  }
}
