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

  let t = Cil.typeOf full_expr in
  let size = bitsSizeOf t
  in if (size > 64) then (
      (* should never happen *)
    rte_warn fmt_warn_bitsize_over_64 d_exp full_expr ;
    []
  )
    else
      let (minType,maxType) = (min_signed_number size, max_signed_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_le ()   = assertion_le (full_add_term ()) maxType
      and assertion_ge ()   = assertion_ge (full_add_term ()) minType in
      let full_assertion () = 
        Logic_const.pand (assertion_le (), assertion_ge ())
      in
      if simplify_constants then begin
        match get_expr_val expr1, get_expr_val expr2 with
        | Some a64, Some b64 -> (* both operands are constant *)
          let big_a64 = a64
          and big_b64 = b64
          in
          if op = MinusA then
            let big_diff = Int.sub big_a64 big_b64
            in if Int.lt big_diff minType then
                let assertion = assertion_ge () in
                if warning then
                  rte_warn
                    fmt_signed_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_le () in
              if warning then
                rte_warn
                  fmt_signed_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 b_ov = Int.gt big_mult maxType in
            if b_ov then
              let assertion = assertion_le () in
              if warning then
                rte_warn
                  fmt_signed_overflow_assert
                  d_predicate_named assertion ;
              [ assertion, Some Property_status.False_if_reachable ]
            else
              let b_uv = Int.lt big_mult minType in
              if b_uv then
                let assertion = assertion_ge () in
                if warning then
                  rte_warn
                    fmt_signed_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 = MinusA then
            [ assertion_ge (), None ]
          else if op = PlusA then
            [ assertion_le (), None ]
          else begin
            assert(op = Mult);
                  (* 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
                    (* multiplying by -1 is dangerous (albeit seldom) *)
              if My_bigint.equal a64 My_bigint.minus_one then
                [ assertion_le (), None ]
              else 
                [ full_assertion (), None ]
          end
        | None,None -> 
              (* no operand is a constant *)
          [ full_assertion (), None ]
      end else
        [ full_assertion (), None ]