let reduce_by_cond ~with_alarms state cond =
  (* Do not reduce anything if the cond is volatile.
     (This test is dumb because the cond may contain volatile l-values
     without the "volatile" attribute appearing at toplevel. pc 2007/11) *)

(*  Format.printf "eval_cond %B %a@." cond.positive (!d_exp) cond.exp; *)
  if hasAttribute "volatile" (typeAttr (typeOf cond.exp)) then state
  else
    let rec aux cond state =
      match cond.positive,cond.exp.enode with
      | _positive, BinOp ((Le|Ne|Eq|Gt|Lt|Ge as binop), exp1, exp2, _typ) ->
        let eval = eval_from_type (unrollType (typeOf exp1))
          (Value_parameters.AllRoundingModes.get ())
        in
        reduce_by_comparison ~with_alarms eval
          cond.positive exp1 binop exp2 state

      | trueBinOp (LAnd, exp1, exp2, _)
      | falseBinOp (LOr, exp1, exp2, _) ->
          let new_state = aux {cond with exp = exp1} state in
          let result = aux {cond with exp = exp2} new_state in
          result
      | falseBinOp (LAnd, exp1, exp2, _)
      | trueBinOp (LOr, exp1, exp2, _) ->
          let new_v1 = try aux {cond with exp = exp1} state
            with Reduce_to_bottom -> Cvalue.Model.bottom
          in let new_v2 = try aux {cond with exp = exp2} state
            with Reduce_to_bottom -> Cvalue.Model.bottom
          in let r = Cvalue.Model.join new_v1 new_v2 in
          if Db.Value.is_reachable r then r else raise Reduce_to_bottom

      | _, UnOp(LNot,exp,_) ->
          aux { positive = not cond.positive; exp = exp; } state
      | _, Lval _ when (let t = typeOf cond.exp in
                        isIntegralType t || isPointerType t)
          -> (* "if (c)" is equivalent to "if(!(c==0))" *)
        (try
           let loc,value_for_loc,_ =
             eval_as_exact_loc ~with_alarms state cond.exp in
           let new_value = eval_symetric_int
             (not cond.positive) Eq V.singleton_zero value_for_loc
           in
           if V.equal new_value V.bottom then raise Reduce_to_bottom
           else 
             Cvalue.Model.reduce_binding 
               ~with_alarms:CilE.warn_none_mode state loc new_value
         with Not_an_exact_loc -> state)
      | _ -> state
    in
    let result = aux cond state in
    let contains_zero =
      if Value_parameters.UndefinedPointerComparisonPropagateAll.get()
      then V.contains_zero
      else (fun x -> V.is_included V.singleton_zero x)
    in
    let condition_may_still_be_true_in_state state =
      let cond_interp = eval_expr ~with_alarms state cond.exp in
      (not cond.positive || V.contains_non_zero cond_interp) &&
        (cond.positive || contains_zero cond_interp)
    in
    if (not (Cvalue.Model.equal result state)) &&
      (not (condition_may_still_be_true_in_state result))
    then raise Reduce_to_bottom;
    let is_enumerable v =
      let v_interp =
        Cvalue.Model.find ~conflate_bottom:true ~with_alarms result v
      in
      ignore (Location_Bytes.cardinal_less_than v_interp 7);
      v_interp
    in
    let rec enumerate_one_var l =
      match l with
      | [] -> raise Not_found
      | v::t ->
          try
            let v_interp = is_enumerable v in
            v,v_interp,t
          with Abstract_interp.Not_less_than ->
            enumerate_one_var t
    in
    let invert_cond vl =
      try
        let v1,v_interp1, _tail = enumerate_one_var vl in
(*      Format.printf "enumerate %a %a@."
          Locations.pretty v1
          Cvalue.V.pretty v_interp1; *)

        let f one_val acc =
          (* interpret cond in an environment where v -> one_val
          *)

          let env =
            Cvalue.Model.reduce_binding
              ~with_alarms:CilE.warn_none_mode
              result v1 one_val
          in
          let stays = condition_may_still_be_true_in_state env in
(*        Format.printf "enumerate %a stays:%B@."
            Cvalue.V.pretty one_val
            stays; *)

          if stays
          then begin
              Location_Bytes.join one_val acc
            end
          else begin
              acc
            end
        in
        let new_v_interp =
          Location_Bytes.fold_enum
            ~split_non_enumerable:2
            f v_interp1 Location_Bytes.bottom
        in
        let state_value =
          if V.equal new_v_interp V.bottom
          then raise Reduce_to_bottom
          else 
            Cvalue.Model.reduce_binding
              ~with_alarms:CilE.warn_none_mode
              result v1 new_v_interp
        in
        state_value
      with Not_found -> result
    in
    let result =
      invert_cond (get_influential_vars ~with_alarms result cond.exp)
    in
    if not (Cvalue.Model.is_reachable result)
    then raise Reduce_to_bottom
    else result