// Bitvectors.dfy

/*
module _System {
  /* CALL GRAPH for module _System:
   * SCC at height 2:
   *   RotateRight
   * SCC at height 1:
   *   RotateLeft
   * SCC at height 0:
   *   nat
   */
  type string(==) = seq<char>

  type {:axiom} nat(==) = x: int
    | 0 <= x

  trait {:compile false} object { }
  /*-- non-null type
  type {:axiom} object(==) = c: object? | c != null    /*special witness*/
  */

  class {:compile false} array<arg> {
    var Length: int  // immutable
  }
  /*-- non-null type
  type {:axiom} array(==)<arg> = c: array?<arg> | c != null    /*special witness*/
  */

  class {:compile false} /*_#Func0*/ () ~> +R {
    function requires(): bool
      reads reads()

    function reads(): set<object?>
      reads reads()
  }

  type {:compile false} /*_#PartialFunc0*/ () --> +R = f: () ~> R
    | f.reads() == {}
    /*special witness*/

  type {:compile false} /*_#TotalFunc0*/ () -> +R = f: () --> R
    | f.requires()
    /*special witness*/

  datatype {:compile false} /*_tuple#2*/ (+T0, +T1) = _#Make2(0: T0, 1: T1)

  class {:compile false} /*_#Func1*/ -T0 ~> +R {
    function requires(x0: T0): bool
      reads reads(x0)

    function reads(x0: T0): set<object?>
      reads reads(x0)
  }

  type {:compile false} /*_#PartialFunc1*/ -T0 --> +R = f: T0 ~> R
    | forall x0: T0 :: f.reads(x0) == {}
    /*special witness*/

  type {:compile false} /*_#TotalFunc1*/ -T0 -> +R = f: T0 --> R
    | forall x0: T0 :: f.requires(x0)
    /*special witness*/

  type bool { }

  type int { }

  type real {
    var Floor: int  // immutable
  }

  type ORDINAL {
    var IsLimit: bool  // immutable
    var IsSucc: bool  // immutable
    var Offset: int  // immutable
    var IsNat: bool  // immutable
  }

  type bv {
    function method RotateLeft(w: nat): selftype

    function method RotateRight(w: nat): selftype
  }

  type map<T, U> {
    var Keys: set<T>  // immutable
    var Values: set<U>  // immutable
    var Items: set<(T, U)>  // immutable
  }

  type imap<T, U> {
    var Keys: iset<T>  // immutable
    var Values: iset<U>  // immutable
    var Items: iset<(T, U)>  // immutable
  }

  datatype /*_tuple#0*/ () = _#Make0
}
// bitvector types in use: bv1 bv32 bv47 bv16 bv0 bv67 bv64 bv53 bv33 bv31 bv15 bv8 bv6 bv2 bv7 bv12
*/

/* CALL GRAPH for module _module:
 * SCC at height 27:
 *   M
 * SCC at height 26:
 *   Main
 * SCC at height 25:
 *   Rotates
 * SCC at height 24:
 *   Shifts
 * SCC at height 23:
 *   Handful
 * SCC at height 22:
 *   TestCompilationTruncations
 * SCC at height 21:
 *   P2
 * SCC at height 20:
 *   M0
 * SCC at height 19:
 *   M1
 * SCC at height 18:
 *   M6
 * SCC at height 17:
 *   M8
 * SCC at height 16:
 *   M15
 * SCC at height 15:
 *   M16
 * SCC at height 14:
 *   M31
 * SCC at height 13:
 *   M32
 * SCC at height 12:
 *   M33
 * SCC at height 11:
 *   M53
 * SCC at height 10:
 *   M64
 * SCC at height 9:
 *   M67
 * SCC at height 8:
 *   Bv0Stuff
 * SCC at height 7:
 *   SummoTests
 * SCC at height 6:
 *   Unary
 * SCC at height 5:
 *   DoArith32
 * SCC at height 4:
 *   Arithmetic
 * SCC at height 3:
 *   BitwiseOperations
 * SCC at height 2:
 *   PrintRotates
 * SCC at height 1:
 *   PrintShifts
 * SCC at height 0:
 *   string
 */
newtype Handful = x: int
  | 0 <= x < 80

method M(a: bv1, b: bv32)
    returns (c: bv32, d: bv1)
  decreases a, b
{
  c := b;
  d := a;
  var x: bv32 := 5000;
  c := x;
  var y: bv32 := 4000;
  y := c;
}

method Main()
{
  var x: bv32 := 4000;
  var y: bv32 := 4000;
  var z: bv32;
  var w: bv32;
  if x == y {
    z := x;
  } else {
    w := y;
  }
  print x, " ", y, " ", z, " ", w, "\n";
  var t: bv47, u: bv47, v: bv47 := BitwiseOperations();
  print t, " ", u, " ", v, "\n";
  DoArith32();
  var unry: bv16 := Unary(5);
  print "bv16: 5 - 2 == ", unry, "\n";
  unry := Unary(1);
  print "bv16: 1 - 2 == ", unry, "\n";
  SummoTests();
  var zz0: bv0;
  var zz1: bv0 := Bv0Stuff(zz0, 0);
  print zz0, " ", zz1, "\n";
  print zz0 < zz1, " ", zz0 <= zz1, " ", zz0 >= zz1, " ", zz0 > zz1, "\n";
  TestCompilationTruncations();
  Shifts();
  Rotates();
}

method BitwiseOperations() returns (a: bv47, b: bv47, c: bv47)
{
  b, c := 2053, 1099;
  a := b & c;
  a := a | a | (b & b & c & (a ^ b ^ c) & a);
}

method Arithmetic(x: bv32, y: bv32)
    returns (r: bv32, s: bv32)
  ensures r == x + y && s == y - x
  decreases x, y
{
  r := x + y;
  s := y - x;
}

method DoArith32()
{
  var r: bv32, s: bv32 := Arithmetic(65, 120);
  print r, " ", s, "\n";
  var x: bv32, y: bv32 := 2147483647, 2147483651;
  r, s := Arithmetic(x, y);
  assert r == 2 && s == 4;
  print r, " ", s, "\n";
  assert x < y && x <= y && y >= x && y > x;
  print "Comparisons: ", x < y, " ", x <= y, " ", x >= y, " ", x > y, "\n";
}

method Unary(x: bv16) returns (y: bv16)
  ensures y == x - 2
  decreases x
{
  y := --!-!!--x;
  y := !-y;
  var F: bv16 := 65535;
  calc == {
    y;
  ==
    !---!-!!--x;
  ==
    F - ---!-!!--x;
  ==
    {
      assert ---!-!!--x == -!-!!--x;
    }
    F - -!-!!--x;
  ==
    F + !-!!--x;
  ==
    F + F - -!!--x;
  ==
    F + F + !!--x;
  ==
    {
      assert !!--x == --x == x;
    }
    F + F + x;
  ==
    x - 2;
  }
}

method SummoTests()
{
  var a: bv64 := 5;
  a := 2 * 2 * 2 * 2 * 2 * a * 2 * 2 * 2 * 2 * 2;
  var b: bv64 := a / 512;
  assert b == 10;
  assert b / 3 == 3 && b / 4 == 2;
  assert b % 3 == 1 && b % 4 == 2;
  print b / 3, " ", b % 4, "\n";
}

method Bv0Stuff(x: bv0, y: bv0) returns (z: bv0)
  ensures z == 0
  decreases x, y
{
  z := x + y;
  z := x * z - y;
  z := (x ^ z) | (y & y);
  z := !z + -z;
}

method TestCompilationTruncations()
{
  M67(-1, 3);
  M64(-1, 3);
  M53(-1, 3);
  M33(-1, 3);
  M32(-1, 3);
  M31(-1, 3);
  M16(-1, 3);
  M15(-1, 3);
  M8(-1, 3);
  M6(-1, 3);
  M1(1, 1);
  M0(0, 0);
  P2(3, 2);
}

method M67(a: bv67, b: bv67)
  decreases a, b
{
  print "bv67:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method M64(a: bv64, b: bv64)
  decreases a, b
{
  print "bv64:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method M53(a: bv53, b: bv53)
  decreases a, b
{
  print "bv53:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method M33(a: bv33, b: bv33)
  decreases a, b
{
  print "bv33:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method M32(a: bv32, b: bv32)
  decreases a, b
{
  print "bv32:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method M31(a: bv31, b: bv31)
  decreases a, b
{
  print "bv31:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method M16(a: bv16, b: bv16)
  decreases a, b
{
  print "bv16:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method M15(a: bv15, b: bv15)
  decreases a, b
{
  print "bv15:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method M8(a: bv8, b: bv8)
  decreases a, b
{
  print "bv8:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method M6(a: bv6, b: bv6)
  decreases a, b
{
  print "bv6:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method M1(a: bv1, b: bv1)
  decreases a, b
{
  print "bv1:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method M0(a: bv0, b: bv0)
  decreases a, b
{
  print "bv0:  ", a, " + ", b, " == ", a + b, "     - ", b, " == ", -b, "     !! ", b, " == ! ", !b, " == ", !!b, "\n";
}

method P2(a: bv2, b: bv2)
  requires b != 0
  decreases a, b
{
  print "bv2:\n";
  print "  ", a, " + ", b, " == ", a + b, "\n";
  print "  ", a, " - ", b, " == ", a - b, "\n";
  print "  ", a, " * ", b, " == ", a * b, "\n";
  print "  ", a, " / ", b, " == ", a / b, "\n";
  print "  ", a, " % ", b, " == ", a % b, "\n";
}

method Shifts()
{
  var x: int, h: Handful, b67: bv67, w: bv32, seven: bv7, bb: bv2, noll: bv0;
  x, h := 3, 3;
  b67, w, seven := 5, 5, 5;
  PrintShifts("bv67", b67, 8 * b67, b67 << x, b67 << h);
  PrintShifts("bv32", w, 8 * w, w << x, w << h);
  PrintShifts("bv7", seven, 8 * seven, seven << x, seven << h);
  bb, x, h := 1, 1, 1;
  PrintShifts("bv2", bb, 2 * bb, bb << x, bb << h);
  x, h := 0, 0;
  PrintShifts("bv0", noll, 0 * noll, noll << x, noll << h);
  b67, w, bb, noll := 73786976294838206465, 1, 1, 0;
  var One67: bv67 := 1;
  PrintShifts("bv67 again", b67 << One67, b67 << w, b67 << bb, b67 << noll);
  b67, w, bb, noll := 2, 3221225472 + 2000, 1, 0;
  var Two32: bv32 := 2;
  PrintShifts("bv32 again", w << b67, w << Two32, w << bb, w << noll);
  seven, b67, w, bb, noll := 127, 2, 2, 2, 0;
  PrintShifts("bv7 again", seven << b67, seven << w, seven << bb, seven << noll);
  b67, w, bb := 0, 0, 0;
  PrintShifts("bv0 again", noll << b67, noll << w, noll << bb, noll << noll);
}

method PrintShifts<T>(s: string, a: T, b: T, c: T, d: T)
  decreases s
{
  print "PrintShifts: ", s, ": ", a, " ", b, " ", c, " ", d, "\n";
}

method Rotates()
{
  var x: int, w: bv12, seven: bv7, bb: bv2, noll: bv0;
  x := 3;
  w, seven, noll := 5, 5, 0;
  PrintRotates("bv12", w, w.RotateLeft(x).RotateRight(x));
  PrintRotates("bv7", seven, seven.RotateLeft(x).RotateRight(x));
  bb, x := 1, 1;
  PrintRotates("bv2", bb, bb.RotateLeft(x).RotateRight(x));
  x := 0;
  PrintRotates("bv0", noll, noll.RotateLeft(x).RotateRight(x));
  x := 5;
  w, seven := 3072 + 2000, 127;
  PrintRotates("bv12 again", w, w.RotateLeft(x).RotateRight(x));
  PrintRotates("bv7 again", seven, seven.RotateLeft(x).RotateRight(x));
}

method PrintRotates<T>(s: string, a: T, b: T)
  decreases s
{
  print "PrintRotates: ", s, ": ", a, " ", b, "\n";
}

Dafny program verifier finished with 11 verified, 0 errors
4000 4000 4000 0
1 2053 1099
185 55
2 4
Comparisons: true true false false
bv16: 5 - 2 == 3
bv16: 1 - 2 == 65535
3 2
0 0
false true true false
bv67:  147573952589676412927 + 3 == 2     - 3 == 147573952589676412925     !! 3 == ! 147573952589676412924 == 3
bv64:  18446744073709551615 + 3 == 2     - 3 == 18446744073709551613     !! 3 == ! 18446744073709551612 == 3
bv53:  9007199254740991 + 3 == 2     - 3 == 9007199254740989     !! 3 == ! 9007199254740988 == 3
bv33:  8589934591 + 3 == 2     - 3 == 8589934589     !! 3 == ! 8589934588 == 3
bv32:  4294967295 + 3 == 2     - 3 == 4294967293     !! 3 == ! 4294967292 == 3
bv31:  2147483647 + 3 == 2     - 3 == 2147483645     !! 3 == ! 2147483644 == 3
bv16:  65535 + 3 == 2     - 3 == 65533     !! 3 == ! 65532 == 3
bv15:  32767 + 3 == 2     - 3 == 32765     !! 3 == ! 32764 == 3
bv8:  255 + 3 == 2     - 3 == 253     !! 3 == ! 252 == 3
bv6:  63 + 3 == 2     - 3 == 61     !! 3 == ! 60 == 3
bv1:  1 + 1 == 0     - 1 == 1     !! 1 == ! 0 == 1
bv0:  0 + 0 == 0     - 0 == 0     !! 0 == ! 0 == 0
bv2:
  3 + 2 == 1
  3 - 2 == 1
  3 * 2 == 2
  3 / 2 == 1
  3 % 2 == 1
PrintShifts: bv67: 5 40 40 40
PrintShifts: bv32: 5 40 40 40
PrintShifts: bv7: 5 40 40 40
PrintShifts: bv2: 1 2 2 2
PrintShifts: bv0: 0 0 0 0
PrintShifts: bv67 again: 2 2 2 73786976294838206465
PrintShifts: bv32 again: 8000 8000 2147487648 3221227472
PrintShifts: bv7 again: 124 124 124 127
PrintShifts: bv0 again: 0 0 0 0
PrintRotates: bv12: 5 5
PrintRotates: bv7: 5 5
PrintRotates: bv2: 1 1
PrintRotates: bv0: 0 0
PrintRotates: bv12 again: 976 976
PrintRotates: bv7 again: 127 127
