module Test_proofinuse

  use int.Int
  use int.Power
  use int.EuclideanDivision
  use bv.BV32

  (* Shift is div example -------------------  *)

  let shift_is_div ( x : t ) ( y : t ) : t
    requires { 0 <= t'int y < 32 }
  =
    let res = lsr_bv x 0x1 in
    assert { res = udiv x (2:t) };
    assert { t'int res = div (t'int x) 2 };
    let res : t = lsr_bv res (2:t) in
(*
    assert { res = udiv x (8:t) };
    assert { t'int res = div (t'int x) 8 };
*)
    res

 (* Mask example --------------------- *)

  use bv.BV8 as BV8
  use bv.BV64 as BV64

  use bv.BVConverter_32_64 as C32_46
  use bv.BVConverter_8_32 as C8_32

  type bitvec8 = BV8.t
  type bitvec64 = BV64.t

  let mask ( x : t ) =
    ensures { result = BV8.one }
    (* ensures{ not ( BV8.eq result (BV8.of_int 1) ) } *)
    let res = C8_32.toSmall(
                bw_and
                  (bw_or
                    (bw_and x (0xFFFF_FF00:t))
                    0x1)
                  (0x0000_00FF:t))
    in res

  (* test invariant from frama-c ---------------- *)

  use ref.Ref

  let testVariant( n : t ) =
    let i = ref n in
    let two = 2:t in

    while uge !i two do
    variant{ t'int !i }
      i := lsr_bv !i two
    done

  (* testssss *)

  predicate in_range (n : t) = (ule (0:t) n) /\ (ule n (0x0000_00FF:t))

  predicate add_over (x y : t) = (uge (add x y) x) /\ (uge (add x y) y)

  lemma ttt: forall x y.  (add_over x y) -> (forall i. ult i x -> add_over i y)
end

(** {1 Examples from Hackers Delight} *)

theory Hackers_delight
  use int.Int
  use bv.BV32

  (** de morgan's laws *)

  goal DM1: forall x y.
    bw_not( bw_and x y ) = bw_or (bw_not x) (bw_not y)

  goal DM2: forall x y.
    bw_not( bw_or x y ) = bw_and (bw_not x) (bw_not y)

  goal DM3: forall x.
    bw_not (add x one) = sub (bw_not x) one

  goal DM4: forall x.
    bw_not (sub x one) = add (bw_not x) one

  goal DM5: forall x.
    bw_not (neg x) = sub x one

  goal DM6: forall x y.
    bw_not( bw_xor x y ) = bw_xor (bw_not x) y (* = eq x y *)

  goal DM7: forall x y.
    bw_not( add x y ) = sub (bw_not x) y

  goal DM8: forall x y.
    bw_not( sub x y ) = add (bw_not x) y

  goal DMtest: forall x.
    zeros = bw_not( bw_or x (neg( add x one)))

  (* inequality  *)

  goal IE1: forall x y.
    ule (bw_xor x y) (bw_or x y)

  goal IE2: forall x y.
    ule (bw_and x y) (bw_not( bw_xor x y ))

  goal IE3: forall x y.
    ( ule x (add x y) /\ ule y (add x y) ) -> ule (bw_or x y) (add x y)

  goal IE4: forall x y.
    not ( ule x (add x y) /\ ule y (add x y) ) -> ugt (bw_or x y) (add x y)

  (* shift right and arithmetic shift right *)

  goal SR1: forall x n. ( ule zeros n /\ ule n (31:t)) ->
    bw_or (lsr_bv x n) (lsl_bv (neg( lsr_bv x (31:t) )) (sub (31:t) n))
  = asr_bv x n

  (* rotate en shift *)

  goal RS_left: forall x.
    bw_or (lsl_bv x one) (lsr_bv x (31:t)) = rotate_left_bv x one

  goal RS_right: forall x.
    bw_or (lsr_bv x one) (lsl_bv x (31:t)) = rotate_right_bv x one

  (* bound propagation *)

  goal BP1: forall a b c d x y.
    ( ule a x /\ ule x b /\ ule c y /\ ule y d ) ->
    ( ule b (add b d) /\ ule d (add b d) ) -> (* no overflow in addition *)
      ule (bw_or x y) (add b d) /\ ule zeros (bw_and x y)

  goal BP2: forall a b c d x y.
    ( ule a x /\ ule x b /\ ule c y /\ ule y d ) ->
    ( ule b (add b d) /\ ule d (add b d) ) -> (* no overflow in addition *)
      ule zeros (bw_xor x y) /\ ule (bw_xor x y) (add b d)

  goal BP3: forall a b c d x y.
    ( ule a x /\ ule x b /\ ule c y /\ ule y d ) ->
      ule (bw_not b) (bw_not x) /\ ule (bw_not x) (bw_not a)

end

module Hackers_delight_mod
  use int.Int
  use bv.BV32

  (* de morgan's laws *)

  let dm1 (x : t) (y : t) =
    ensures{ result = bw_or (bw_not x) (bw_not y) }
    bw_not( bw_and x y )

  let dm2 (x : t) (y : t) =
    ensures{ result = bw_and (bw_not x) (bw_not y) }
    bw_not( bw_or x y )

  let dm3 (x : t) =
    ensures{ result = sub (bw_not x) one }
    bw_not( add x 0x1 )

  let dm4 (x : t) =
    ensures{ result = add (bw_not x) one }
    bw_not( sub x 0x1 )

  let dm5 (x : t) =
    ensures{ result = sub x one }
    bw_not( neg x )

  let dm6 (x : t) (y : t) =
    ensures{ result = bw_xor (bw_not x) y }
    bw_not( bw_xor x y )

  let dm7 (x : t) (y : t) =
    ensures{ result = sub (bw_not x) y }
    bw_not( add x y )

  let dm8 (x : t) (y : t) =
    ensures{ result = add (bw_not x) y }
    bw_not( sub x y )

  let dmtest (x : t) =
    ensures{ result = zeros }
    bw_not( bw_or x (neg( add x 0x1)))

  (* inequality  *)

  let ie1 (x : t) (y : t) =
    ensures{ ule result (bw_or x y) }
    bw_xor x y

  let ie2 (x : t) (y : t) =
    ensures{ ule result (bw_not( bw_xor x y ))}
    bw_and x y

  let ie3 (x : t) (y : t) =
    requires{ ule x (add x y) /\ ule y (add x y) }
    ensures{ ule result (add x y) }
    bw_or x y

  let ie4 (x : t) (y : t) =
    requires{ not ( ule x (add x y) /\ ule y (add x y) ) }
    ensures{ ugt result (add x y) }
    bw_or x y

  (* shift right and arithmetic shift right *)

  let sr1 (x : t) (n : t) =
    requires{ ule zeros n /\ ule n (31:t) }
    ensures{ result = asr_bv x n }
    bw_or (lsr_bv x n) (lsl_bv (neg( lsr_bv x (31:t) )) (sub (31:t) n))

  (* rotate en shift *)

  let rs_left (x : t) =
  ensures{ result = rotate_left_bv x one }
    bw_or (lsl_bv x 0x1) (lsr_bv x (31:t))

  let rs_right (x : t) =
  ensures{ result = rotate_right_bv x one }
    bw_or (lsr_bv x 0x1) (lsl_bv x (31:t))

  (* bound propagation *)

  let bp1 (a b c d x y : t) =
  requires{ ule a x /\ ule x b }
  requires{ ule c y /\ ule y d }
  requires{ ule b (add b d) /\ ule d (add b d) } (* no overflow in addition *)
  ensures{ ule result (add b d) }
    bw_or x y

  let bp1' (a b c d x y : t) =
  requires{ ule a x /\ ule x b }
  requires{ ule c y /\ ule y d }
  requires{ ule b (add b d) /\ ule d (add b d) } (* no overflow in addition *)
  ensures{ ule zeros result }
    bw_and x y

  let bp2 (a b c d x y : t) =
  requires{ ule a x /\ ule x b }
  requires{ ule c y /\ ule y d }
  requires{ ule b (add b d) /\ ule d (add b d) } (* no overflow in addition *)
  ensures{ ule zeros result }
  ensures{ ule result (add b d) }
    bw_xor x y

  let bp3 (a b c d x y : t) =
  requires{ ule a x /\ ule x b }
  requires{ ule c y /\ ule y d }
  ensures{ ule (bw_not b) result }
  ensures{ ule result (bw_not a) }
    bw_not x

end

module Test_imperial_violet

  use int.Int
  use int.EuclideanDivision
  use bv.BV32
  use array.Array

  (* to_int and bounds *)

  lemma bv32_bounds_bv:
    forall b. ule zeros b /\ ule b ones

  lemma to_int_ule:
    forall b c. ule b c -> t'int b <= t'int c

  lemma to_int_ult:
    forall b c. ult b c -> t'int b < t'int c

  lemma bv32_bounds_0:
    forall b. 0 <= t'int b

  lemma bv32_bounds:
    forall b. 0 <= t'int b < 0x1_0000_0000

  (* bounded add of array of t *)

  let add (a : array t ) (b : array t) =
    requires{ length a = length b }
    requires{ forall i. 0 <= i < length a ->
                ult a[i] (0x8000_0000:t) }
    requires{ forall i. 0 <= i < length b ->
                ult b[i] (0x8000_0000:t) }
    ensures{ forall i. 0 <= i < length result ->
               t'int result[i] = t'int a[i] + t'int b[i] }
    let sum = make (length a) 0x0 in
    for i = 0 to length a - 1 do
      invariant{ forall j. 0 <= j < i -> sum[j] = add a[j] b[j] }
      invariant{ forall j. 0 <= j < i -> t'int sum[j] = t'int a[j] + t'int b[j] }
      sum[i] <- add a[i] b[i]
    done;
    sum

end

module Test_from_bitvector_example

  use int.Int
  use bv.BV32

  goal Test1:
    let b = bw_and zeros ones in nth_bv b one = False

  goal Test2:
    let b = lsr_bv ones (16:t) in nth_bv b (15:t) = True

  goal Test3:
    let b = lsr_bv ones (16:t) in nth_bv b (16:t) = False

  goal Test4:
    let b = asr_bv ones (16:t) in nth_bv b (15:t) = True

  goal Test5:
    let b = asr_bv ones (16:t) in nth_bv b (16:t) = True

  goal Test6:
    let b = asr_bv (lsr_bv ones one) (16:t) in nth_bv b (16:t) = False

  let lsr31 () =
    ensures{ result = one }
    lsr_bv 0xFFFF_FFFF (31:t)

  let lsr30 () =
    ensures{ result = (3:t) }
    lsr_bv 0xFFFF_FFFF (30:t)

  let lsr29 () =
    ensures{ t'int result = 7 }
    lsr_bv 0xFFFF_FFFF (29:t)

  let lsr28 () =
    ensures{ t'int result = 15 }
    lsr_bv 0xFFFF_FFFF (28:t)

  let lsr27 () =
    ensures{ t'int result = 31 }
    lsr_bv 0xFFFF_FFFF (27:t)

  let lsr26 () =
    ensures{ t'int result = 63 }
    lsr_bv 0xFFFF_FFFF (26:t)

  let lsr20 () =
    ensures{ t'int result = 4095 }
    lsr_bv 0xFFFF_FFFF (20:t)

  let lsr13 () =
    ensures{ t'int result = 524287 }
    lsr_bv 0xFFFF_FFFF (13:t)

  let lsr8 () =
    ensures{ t'int result = 16777215 }
    lsr_bv 0xFFFF_FFFF (8:t)

  goal to_int_0x00000001:
    t'int (lsr_bv ones (31:t)) = 1

  goal to_int_0x00000003:
    t'int (lsr_bv ones (30:t)) = 3

  goal to_int_0x00000007:
    t'int (lsr_bv ones (29:t)) = 7

  goal to_int_0x0000000F:
    t'int (lsr_bv ones (28:t)) = 15

  goal to_int_0x0000001F:
    t'int (lsr_bv ones (27:t)) = 31

  goal to_int_0x0000003F:
    t'int (lsr_bv ones (26:t)) = 63

  goal to_int_0x0000007F:
    t'int (lsr_bv ones (25:t)) = 127

  goal to_int_0x000000FF:
    t'int (lsr_bv ones (24:t)) = 255

  goal to_int_0x000001FF:
    t'int (lsr_bv ones (23:t)) = 511

  goal to_int_0x000003FF:
    t'int (lsr_bv ones (22:t)) = 1023

  goal to_int_0x000007FF:
    t'int (lsr_bv ones (21:t)) = 2047

  goal to_int_0x00000FFF:
    t'int (lsr_bv ones (20:t)) = 4095

  goal to_int_0x00001FFF:
    t'int (lsr_bv ones (19:t)) = 8191

  goal to_int_0x00003FFF:
    t'int (lsr_bv ones (18:t)) = 16383

  goal to_int_0x00007FFF:
    t'int (lsr_bv ones (17:t)) = 32767

  goal to_int_0x0000FFFF:
    t'int (lsr_bv ones (16:t)) = 65535

  goal to_int_0x0001FFFF:
    t'int (lsr_bv ones (15:t)) = 131071

  goal to_int_0x0003FFFF:
    t'int (lsr_bv ones (14:t)) = 262143

  goal to_int_0x0007FFFF:
    t'int (lsr_bv ones (13:t)) = 524287

  goal to_int_0x000FFFFF:
    t'int (lsr_bv ones (12:t)) = 1048575

  goal to_int_0x00FFFFFF:
    t'int (lsr_bv ones (8:t)) = 16777215

  goal to_int_0xFFFFFFFF:
    t'int ones = 4294967295

end
