let get_multsubadd_unsigned_assertion
    ~simplify_constants:simplify_constants
    ~warning:warning
    full_expr op expr1 expr2 =
  (* unsigned multiplication/addition/subtraction:
     the expression overflows iff its integer value
     is strictly more than TYPE_MAX or strictly less than TYPE_MIN (=0) *)

  let t = Cil.typeOf full_expr in
  let size = bitsSizeOf t
  in if (size > 32) then (
      (* could happen: but then it's not possible yet to represent the maximum
         possible value of the domain (2^64 - 1) as a Cil constant
         (see TODO in cil_types.mli)
      *)

      rte_warn fmt_warn_bitsize_over_32 d_exp full_expr ;
    []
  )
    else
      let (minType,maxType) = My_bigint.zero, max_unsigned_number size in
      let full_add_term () =
        (* no cast to int since we check "true result" which is an integer *)
        let term1 = translate_C_expr_to_term ~cast:false expr1
        and term2 = translate_C_expr_to_term ~cast:false expr2
        in Logic_const.term (TBinOp (op, term1,term2)) (Ctype t)
      in let assertion () =
           if op = MinusA then
             assertion_ge (full_add_term ()) minType
           else
             assertion_le (full_add_term ()) maxType
         in
         if simplify_constants then begin
           match get_expr_val expr1, get_expr_val expr2 with
           | Some big_a64, Some big_b64 -> (* both operands are constant *)
             if op = MinusA then
               let big_diff = Int.sub big_a64 big_b64
               in
               if Int.lt big_diff minType then
                 let assertion = assertion () in
                 if warning then
                   rte_warn
                     fmt_unsigned_overflow_assert
                     d_predicate_named assertion;
                 [ assertion, Some Property_status.False_if_reachable ]
               else [ ]
             else if op = PlusA then
               let big_add = Int.add big_a64 big_b64 in
               if Int.gt big_add maxType then
                 let assertion = assertion () in
                 if warning then
                   rte_warn
                     fmt_unsigned_overflow_assert
                     d_predicate_named assertion;
                 [ assertion, Some Property_status.False_if_reachable ]
               else [ ]
             else (
               assert(op = Mult) ;
               let big_mult = Int.mul big_a64 big_b64 in
               let () = assert(Int.compare big_mult Int.zero >= 0) in
               let b_ov = Int.gt big_mult maxType in
               if b_ov then
                 let assertion = assertion () in
                 if warning then
                   rte_warn
                     fmt_unsigned_overflow_assert
                     d_predicate_named assertion ;
                 [ assertion, Some Property_status.False_if_reachable ]
               else [ ])
           | Some a64, None
           | NoneSome a64 ->  (* one operand is constant *)
             if op = Mult then begin
               (* multiplying by 1 or 0 if not dangerous *)
               if My_bigint.equal a64 My_bigint.zero
                 || My_bigint.equal a64 My_bigint.one
               then []
               else [ assertion (), None ]
             end else [ assertion (), None ]
           | NoneNone -> 
             (* no operand is a constant *)
             [ assertion (), None ]
         end else
           [ assertion (), None ]