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 "bitsSize of %a > 32: not treated" d_exp full_expr ;
      []
    )
    else
      let (minType,maxType) = (Int64.zero, get_unsigned_max 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 (
          match get_expr_val expr1, get_expr_val expr2 with
            | Some a64, Some b64 -> (* both operands are constant *)
                let big_a64 = Int.of_int64 a64
                and big_b64 = Int.of_int64 b64
                in
                  if op = MinusA then
                    let big_diff = Int.sub big_a64 big_b64
                    in if Int.compare big_diff (Int.of_int64 minType) < 0 then
                        let assertion = assertion () in
                          if warning then
                            rte_warn
                              "unsigned overflow assert broken: %a"
                              d_predicate_named assertion
                          ;
                          [ (assertion, Some (make_check_false ())) ]
                      else [ ]
                  else if op = PlusA then
                    let big_add = Int.add big_a64 big_b64
                    in if Int.compare big_add (Int.of_int64 maxType) > 0 then
                        let assertion = assertion () in
                          if warning then
                            rte_warn
                              "unsigned overflow assert broken: %a"
                              d_predicate_named assertion
                          ;
                          [ (assertion, Some (make_check_false ())) ]
                      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.compare big_mult (Int.of_int64 maxType) > 0)
                    in
                      if b_ov then
                        let assertion = assertion () in
                          if warning then
                            rte_warn
                              "unsigned overflow assert broken: %a"
                              d_predicate_named assertion
                          ;
                          [ (assertion, Some (make_check_false ())) ]
                      else [ ]
                  )
            | Some a64, None
            | NoneSome a64 ->  (* one operand is constant *)
                if op = Mult then (
                  (* multiplying by 1 or 0 if not dangerous *)
                  if (Int64.compare a64 Int64.zero = 0) ||
                    (Int64.compare a64 Int64.one = 0)
                  then []
                  else [ (assertion (), None) ]
                ) else [ (assertion (), None) ]
            | None,None -> [ (assertion (), None) ] (* no operand is a constant *)
        ) else [ (assertion (), None) ]