packages feed

liquid-fixpoint-0.1.0.0: external/fixpoint/Simplification.ml

module C = FixConstraint
module P = Ast.Predicate
module E = Ast.Expression
module Sy = Ast.Symbol
module Su = Ast.Subst

module Misc = FixMisc open Misc.Ops

let rec defs_of_pred (edefs, pdefs) ((p, _) as pred) = 
  match p with
    | Ast.Atom ((Ast.Var v, _), Ast.Eq, e) when not(P.is_tauto pred) -> Sy.SMap.add v e edefs, pdefs
    | Ast.And [Ast.Imp ((Ast.Bexp (Ast.Var v1, _), _), p1), _; 
	       Ast.Imp (p2, (Ast.Bexp (Ast.Var v2, _), _)), _] when v1 = v2 && p1 = p2 && not(P.is_tauto pred) -> 
	edefs, Sy.SMap.add v1 p1 pdefs
    | Ast.And preds -> List.fold_left defs_of_pred (edefs, pdefs) preds
    | _ -> edefs, pdefs

let some_def_applied = ref false
let rec expr_apply_defs edefs pdefs ((e, _) as expr) = 
  let current_some_def_applied = !some_def_applied in
    some_def_applied := false;
    let expr'' =
      match e with
	| Ast.Con _ -> expr
	| Ast.Var v -> 
	    begin
	      try
		let expr' = Sy.SMap.find v edefs in
		  some_def_applied := true;
		  expr'
	      with Not_found -> expr
	    end
	| Ast.App (v, es) -> 
	    let edefs' = Sy.SMap.remove v edefs in
	      Ast.eApp (v, List.map (expr_apply_defs edefs' pdefs) es)
	| Ast.Bin (e1, op, e2) -> 
	    Ast.eBin (expr_apply_defs edefs pdefs e1, op, expr_apply_defs edefs pdefs e2)
	| Ast.Ite (p, e1, e2) -> 
	    Ast.eIte (pred_apply_defs edefs pdefs p, 
		      expr_apply_defs edefs pdefs e1,
		      expr_apply_defs edefs pdefs e2)
	| Ast.Fld (v, e) -> 
	    let v' = 
	      try
		match Sy.SMap.find v edefs with
		  | (Ast.Var v'', _) -> 
		      some_def_applied := true;
		      v''
		  | _ -> v
	      with Not_found -> v
	    in
	      Ast.eFld (v', expr_apply_defs edefs pdefs e)
        | _ -> assertf "Simplification.expr_apply_defs TODO" 
    in
      if !some_def_applied then
	let expr''' = expr_apply_defs edefs pdefs expr'' in
	  some_def_applied := current_some_def_applied;
	  expr'''
      else
	begin
	  some_def_applied := current_some_def_applied;
	  expr''
	end

and pred_apply_defs edefs pdefs ((p, _) as pred) =
  let current_some_def_applied = !some_def_applied in
    some_def_applied := false;
    let pred'' =
      match p with
	| Ast.And ps -> List.map (pred_apply_defs edefs pdefs) ps |> Ast.pAnd
	| Ast.Or ps -> List.map (pred_apply_defs edefs pdefs) ps |> Ast.pOr
	| Ast.Not p -> pred_apply_defs edefs pdefs p |> Ast.pNot
	| Ast.Imp (p, q) -> Ast.pImp (pred_apply_defs edefs pdefs p, pred_apply_defs edefs pdefs q)
	| Ast.Bexp (Ast.Var v, _) ->
	    begin
	      try
		let expr' = Sy.SMap.find v edefs in
		  some_def_applied := true;
		  Ast.pBexp expr'
	      with Not_found ->
		try
		  let pred' = Sy.SMap.find v pdefs in
		    some_def_applied := true;
		    pred'
		with Not_found ->
		  pred
	    end
	| Ast.Atom (e1, brel, e2) ->
	    Ast.pAtom (expr_apply_defs edefs pdefs e1, brel, expr_apply_defs edefs pdefs e2)
	| Ast.Forall (qs, p) ->
	    let vs = List.map fst qs in
	    let edefs' = List.fold_left (fun defs v -> Sy.SMap.remove v defs) edefs vs in
	    let pdefs' = List.fold_left (fun defs v -> Sy.SMap.remove v defs) pdefs vs in
	      Ast.pForall (qs, pred_apply_defs edefs' pdefs' p)
	| _ -> pred
    in
      if !some_def_applied then
	let pred''' = pred_apply_defs edefs pdefs pred'' in
	  some_def_applied := current_some_def_applied;
	  pred'''
      else 
	begin
	  some_def_applied := current_some_def_applied;
	  pred''
	end

let subs_apply_defs edefs pdefs subs =
  List.map (fun (s, e) -> s, expr_apply_defs edefs pdefs e) subs

let kvar_apply_defs edefs pdefs (subs, sym) = 
  subs_apply_defs edefs pdefs subs, sym

let simplify_subs subs =
  List.filter (fun (s, e) -> not(P.is_tauto (Ast.pAtom (Ast.eVar s, Ast.Eq, e)))) subs

let simplify_kvar (subs, sym) =
  simplify_subs subs, sym

let simplify_t t = 
  let env_ps, pfree_env = (* separate concrete predicates from refinement templates *)
    Sy.SMap.fold 
      (fun bv reft (ps, env) -> 
	 let vv = C.vv_of_reft reft in
	 let bv_expr = Ast.eVar bv in
	 let sort = C.sort_of_reft reft in
	 let reft_ps, reft_ks = C.preds_kvars_of_reft reft in
	   (List.rev_append (List.map (fun p -> P.subst p vv bv_expr) reft_ps) ps,
	    if reft_ks = [] then env else Sy.SMap.add bv (vv, sort, reft_ks) env)
      ) (C.env_of_t t) ([], Sy.SMap.empty) in
  let lhs = C.lhs_of_t t in
  let lhs_vv = C.vv_of_reft lhs in
  let lhs_ps, lhs_ks = C.preds_kvars_of_reft lhs in
  let body_pred = Ast.pAnd (C.grd_of_t t :: List.rev_append lhs_ps env_ps) in
  let edefs, pdefs = defs_of_pred (Sy.SMap.empty, Sy.SMap.empty) body_pred in
    (*
    Printf.printf "\nbody_pred edefs map for %d\n" (C.id_of_t t);
    Sy.SMap.iter (fun v exp ->
		    Printf.printf "%s -> %s\n" (Sy.to_string v) (E.to_string exp)
		 ) edefs;
    Printf.printf "edef for lhs_vv %s = %s (simplified %s)\n" (Sy.to_string lhs_vv) 
      (try Sy.SMap.find lhs_vv edefs |> E.to_string with Not_found -> "none")
      (try 
	 Sy.SMap.find lhs_vv edefs 
         |> expr_apply_defs edefs pdefs 
	 |> E.to_string with Not_found -> "none");
    *)
  let kvar_to_simple_Kvar (subs, sym) = C.Kvar (subs |> Su.to_list |> subs_apply_defs edefs pdefs |> simplify_subs |> Su.of_list, sym) in
  let senv = 
    Sy.SMap.mapi (fun bv (vv, sort, ks) -> 
		    List.map kvar_to_simple_Kvar ks |>	C.make_reft vv sort) pfree_env in
(*     Printf.printf "body_pred: %s\n" (P.to_string body_pred); *)
  let sgrd' = pred_apply_defs edefs pdefs body_pred |> Ast.simplify_pred in
  let sgrd = 
    try
      Ast.pAnd [sgrd'; Ast.pAtom (Ast.eVar lhs_vv, Ast.Eq, Sy.SMap.find lhs_vv edefs |> expr_apply_defs edefs pdefs)]
    with Not_found -> sgrd' in
(*    Printf.printf "simplified body_pred: %s\n" (P.to_string sgrd); *)
  let slhs = List.map kvar_to_simple_Kvar lhs_ks |> C.make_reft (C.vv_of_reft lhs) (C.sort_of_reft lhs) in
  let rhs = C.rhs_of_t t in
  let rhs_ps, rhs_ks = C.preds_kvars_of_reft rhs in
  let srhs_pred = pred_apply_defs edefs pdefs (Ast.pAnd rhs_ps) |> Ast.simplify_pred in
  let srhs_ks = List.map kvar_to_simple_Kvar rhs_ks in
  let srhs =  (if P.is_tauto srhs_pred then srhs_ks else (C.Conc srhs_pred) :: srhs_ks) |> 
      C.make_reft (C.vv_of_reft rhs) (C.sort_of_reft rhs) in
    C.make_t senv sgrd slhs srhs (Some (C.id_of_t t)) (C.tag_of_t t)

let simplify_ts ts =
  (* drop t if its rhs is a k variable that is not read *)
  let ts_sofar = ref ts in
  let pruned = ref true in
    while !pruned && !ts_sofar <> [] do
      let pruned_ts, rest_ts = 
	List.partition
	  (fun t ->
	     match C.rhs_of_t t |> C.preds_kvars_of_reft with
	       | [], [(_, sy)] ->
		   List.for_all 
		     (fun t' -> 
			List.for_all (fun (_, sy') -> sy <> sy') 
			  (Sy.SMap.fold 
			     (fun _ reft sofar -> List.rev_append (C.kvars_of_reft reft) sofar) 
			     (C.env_of_t t') (C.lhs_of_t t' |> C.kvars_of_reft))
		     ) !ts_sofar
	       | _ -> false
	  ) !ts_sofar in
	ts_sofar := rest_ts;
	pruned := pruned_ts <> []
    done;
    !ts_sofar

let is_tauto_t t =
  match C.rhs_of_t t |> C.ras_of_reft with
    | [] -> true
    | [C.Conc p] -> P.is_tauto p 
    | _ -> false