let get_bitwise_shift_assertion
    ~simplify_constants:simplify_constants
    ~warning:warning
    exp shiftop loperand roperand =
  (* - (1) right operand should be nonnegative and
     strictly less than the width of promoted left operand:
     now done by get_bitwise_shift_right_operand_assertion
     - (2) (since signed version) left operand should be nonnegative
     (implementation defined for right shift, undefined for left shift)
     - (3) (for signed left shift) (left operand should be nonnegative: see (2), and)
     result should be representable in result type *)

  let t = Cil.typeOf exp in
  let size = bitsSizeOf t in
  let () =
    if not(size = bitsSizeOf (Cil.typeOf loperand) && size <= 64)
    (* size of result type should be size of left (promoted) operand *)
    then (
      rte_warn fmt_warn_bad_bitsize d_exp exp ;
    )
  in
  let maxValResult =
    (* compute greatest representable "size bits" (signed) integer *)
    max_signed_number size
  (*
    let max64 = Int64.max_int
    and shiftright_value = 64 - size
    in if shiftright_value > 0 then Int64.shift_right max64 shiftright_value else max64 *)

  in
  let assertion_2 () =
    let term = translate_C_expr_to_term loperand in Logic_const.prel (Rge, term, Cil.lzero ())
  and assertion_3 () =
    let term = translate_C_expr_to_term ~cast:false exp in
        (* signed result is representable in result type if loperand
           times 2^roperand (where loperand and roperand are nonnegative),
           which should be equal to term (obtained with a shift),
           is less than the maximal value for the result type *)

        (* no cast to int since we check "true result" which is an integer*)
    Logic_const.prel (Rle, term, Cil.lconstant maxValResult)
  in
  let problem_with_assertion_2 () =
    if simplify_constants then (
      match get_expr_val loperand with
      | None -> (false,false)
      | Some c64 ->
              (* left operand is constant: check it is nonnegative *)
        if My_bigint.ge c64 My_bigint.zero
        then (truetrue(* constant operator and assertion holds *)
        else (true,false(* constant operator and assertion does not hold *)
    ) else (false,false)
  and problem_with_assertion_3 () =
    if simplify_constants then (
      match get_expr_val loperand, get_expr_val roperand with
      | None,_
      | _, None -> (false,false)
      | Some lval64, Some rval64 ->
              (* both operands are constant:
                 check result is representable in result type *)

        if (My_bigint.le lval64 My_bigint.zero)
          || (My_bigint.ge rval64 (My_bigint.of_int 64)) then
          (true,false)(* constant operators and assertion does not hold *)
        else
          let result_true_val = Int.shift_left lval64 rval64 in
          if Int.gt result_true_val maxValResult
          then (true,false)
                (* constant operators and assertion does not hold *)
          else (true,true(* constant operators and assertion holds *))
    else (false,false)
  in
  let proceed_with_assertion_3 lassert =
    if (shiftop = Shiftltthen begin
      match problem_with_assertion_3 () with
      | (true,false->
        let assertion = assertion_3 () in
        if warning then
          rte_warn
            fmt_warn_shift_assert1
            d_predicate_named assertion;
        (assertion, Some Property_status.False_if_reachable)::lassert
      | (true,true)  -> lassert
      | (false,_)    -> (assertion_3 (), None)::lassert
    end else
      lassert
  in
  match problem_with_assertion_2 () with
  | truefalse ->
    let assertion = assertion_2 () in
    if warning then
      rte_warn
        fmt_warn_shift_assert2
        d_predicate_named assertion;
    (* do not proceed with assertion 3: left operand is negative,
       hence result is implementation defined anyway for left shift *)

    [ assertion, Some Property_status.False_if_reachable ]
  | truetrue -> proceed_with_assertion_3 [ ]
  | false, _  -> 
    proceed_with_assertion_3 [ assertion_2 (), None ]