// RUN: %exits-with 4 %verify --relax-definite-assignment "%s" > "%t"
// RUN: %diff "%s.expect" "%t"

trait T1
{
  function Plus (x:int, y:int) : int
    requires x>y
  {
    x + y
  }

  function bb(x:int):int
    requires x>10

  function BodyLess1(a:int) : int
    requires a > 0

  function dd(a:int) : int

  method Testing()
}

class C1 extends T1
{
  function dd(x:int):int
  {
    2
  }

  method Testing()
  {
    var x:int := 11;
    x := bb(x);
  }

  function bb(x:int):int
    requires x >10
  {
    x
  }
  function BodyLess1(bda:int) : int
    requires bda > -10
  {
    2
  }

  method CallBodyLess(x:int)
    requires x > -10
  {
    var k:int := BodyLess1(x);
    assert k==2;
  }
}

trait T2
{
  function F(x: int): int
    requires x < 100
    ensures F(x) < 100

  method M(x: int) returns (y: int)
    requires 0 <= x
    ensures x < y
}

class C2 extends T2
{
  function F(x: int): int
    requires x < 100
    ensures F(x) < 100
  {
    x
  }

  method M(x: int) returns (y: int)
    requires -2000 <= x  // a more permissive precondition than in the interface
    ensures 2*x < y  // a more detailed postcondition than in the interface
  {
    y := 2 * x + 1;
  }
}


trait T3
{
  function F(y: int): int
  function G(y: int): int { 12 }
  method M(y: int)
  method N(y: int) {
    var a:int := 100;
    assert a==100;
  }
}
class C3 extends T3
{
  function NewFunc (a:int, b:int) : int
  {
    a + b
  }
  function F(y: int): int { 20 }
  method M(y: int) {
    var a:int := 100;
    assert a==100;
  }
}

trait t
{
  ghost function f(s2:int):int
    ensures f(s2) > 0
    //requires s != null && s.Length > 1
    //reads s, s2
}

class c extends t
{
  ghost function f(s3:int):int
    ensures f(s3) > 1
    //requires s0 != null && s0.Length > (0)
    //reads s0
  {
    2
  }
}

trait TT
{
  static ghost function M(a:int, b:int) : int
    ensures M(a,b) == a + b
  {
    a + b
  }
}

class CC extends TT
{
  method Testing(a:int,b:int)
  {
    assert TT.M(a,b) == a + b;
  }
}


trait T4
{
  function F(y: int): int

  function G(y: int): int
  {
    100
  }

  method M(y: int) returns (kobra:int)
    requires y > 0
    ensures kobra > 0

  method N(y: int)
  {
    var x: int;
    var y : int;
    y := 10;
    x := 1000;
    y := y + x;
  }
}

class C4 extends T4
{
  function F(y: int): int
  {
    200
  }

  method M(kk:int) returns (ksos:int)
    requires kk > -1
    ensures ksos > 0
  {
    ksos:=10;
  }
}

// regression tests

trait OneTrait
{
  ghost predicate P() { true }
  ghost predicate Q() { true }
  ghost predicate R(x: int) { x < 80 }

  method M() ensures P()
  method N() ensures P()
  method O() returns (r: int) ensures R(r)
  method O'() returns (r: int) ensures P()
}

class OneClass extends OneTrait
{
  method M() ensures P() { }
  method N() ensures Q() { }
  method O() returns (r': int) ensures P()  // error: this postcondition does not imply the one in the trait
  {
    r' := 50;
  }
  method O'() returns (r': int) ensures R(r')
  { }  // error: does not establish postcondition R(r')
}
