let rec reduce_by_predicate ~result ~old state positive p =
  let result =
    match positive,p.content with
    | true,Ptrue | false,Pfalse -> state
    | true,Pfalse | false,Ptrue -> Relations_type.Model.bottom
    | true,Pand (p1,p2 ) | false,Por(p1,p2)->
        reduce_by_predicate ~result ~old
          (reduce_by_predicate ~result ~old state positive p1)
          positive
          p2
    | true,Por (p1,p2 ) | false,Pand (p1, p2) ->
        Relations_type.Model.join
          (reduce_by_predicate ~result ~old state positive p1)
          (reduce_by_predicate ~result ~old state positive p2)

    | true,Pimplies (p1,p2) ->
        Relations_type.Model.join
          (reduce_by_predicate ~result ~old state false p1)
          (reduce_by_predicate ~result ~old state true p2)

    | false,Pimplies (p1,p2) ->
        reduce_by_predicate ~result ~old
          (reduce_by_predicate ~result ~old state true p1)
          false
          p2

    | _,Pnot p -> reduce_by_predicate ~result ~old state (not positive) p
    | true,Piff (p1, p2) ->
        let red1 =
          reduce_by_predicate ~result ~old
            state true (Logic_const.pand (p1, p2))
        in
        let red2 =
          reduce_by_predicate ~result ~old state false
            (Logic_const.por (p1, p2))
        in
        Relations_type.Model.join red1 red2
    | false,Piff (p1, p2) ->
        reduce_by_predicate ~result ~old  state true
          (Logic_const.por
             (Logic_const.pand (p1, Logic_const.pnot p2),
              Logic_const.pand (Logic_const.pnot p1, p2)))
    | _,Pxor(p1,p2) ->
        reduce_by_predicate ~result ~old
          state (not positive) (Logic_const.piff(p1, p2))
    | _,Prel (op,t1,t2) ->
        begin try
          let c1 = !Db.Properties.Interp.term_to_exp ~result t1 in
          let c2 = !Db.Properties.Interp.term_to_exp ~result t2 in
          let t = dummy_exp (BinOp(lop_to_cop op, c1, c2, intType)) in
          let state =
            eval_cond ~with_alarms:warn_raise_mode
              state { positive = positive ; exp = t }
          in
          state
        with
          Invalid_argument "not an lvalue" -> state
        | Reduce_to_bottom ->
            Relations_type.Model.bottom
              (* if the exception was obtained without an alarm emitted,
                 it is correct to return the bottom state *)

        | Predicate_alarm -> state
      end

    | _,Pvalid tsets ->
        begin try
          let exps = !Db.Properties.Interp.loc_to_exp ~result tsets in
          List.fold_left
            (fun state exp ->
               reduce_by_valid_expr ~with_alarms:warn_raise_mode ~positive
                 exp state) state exps
          with Invalid_argument "not an lvalue" -> state
          | Predicate_alarm -> state
        end

    | _,Papp _ | _,Pold _ | _,Pat _ -> state
    | _,Pexists (_varl, _p1) | _,Pforall (_varl, _p1) -> state
    | _,Pfresh _
    | _,Pvalid_range (_, _, _)| _,Pvalid_index (_, _)
    | _,Plet (_, _) | _,Pif (_, _, _)
    | _,Psubtype _
        -> state
    | _, Pseparated _ -> state

  in
  result