https://github.com/EasyCrypt/easycrypt
Revision 6df81b68bb7fb2010d10043e5c11e4401cf56d37 authored by Benjamin Gregoire on 01 April 2014, 07:58:40 UTC, committed by Benjamin Gregoire on 01 April 2014, 07:58:40 UTC
1 parent e43821c
Raw File
Tip revision: 6df81b68bb7fb2010d10043e5c11e4401cf56d37 authored by Benjamin Gregoire on 01 April 2014, 07:58:40 UTC
add product of two and 3 sets
Tip revision: 6df81b6
ecLogic.ml
(* -------------------------------------------------------------------- *)
open EcLocation
open EcUtils
open EcMaps
open EcIdent
open EcPath
open EcCoreLib
open EcTypes
open EcFol
open EcBaseLogic
open EcEnv
open EcReduction

(* -------------------------------------------------------------------- *)
let set_loc loc f x =
  try f x with e -> EcLocation.locate_error loc e

let set_oloc oloc f x =
  match oloc with
  | None     -> f x
  | Some loc -> set_loc loc f x

(* -------------------------------------------------------------------- *)
type pre_judgment = {
  pj_decl : LDecl.hyps * form;
  pj_rule : (bool * int rnode) option;
}

type judgment_uc = {
  juc_count : int;
  juc_map   : pre_judgment Mint.t;
}

type goals  = judgment_uc * int list
type goal   = judgment_uc * int
type tactic = goal -> goals

let new_goal juc decl =
  let n = juc.juc_count in
  let pj = { pj_decl = decl; pj_rule = None; } in
  let juc =
    { juc_count = n + 1;
      juc_map   = Mint.add n pj juc.juc_map } in
  juc, n

let open_juc decl =
  fst (new_goal { juc_count = 0; juc_map = Mint.empty } decl)

let get_pj (juc,n) =
  try Mint.find n juc.juc_map
  with Not_found -> raise (UnknownSubgoal n)

let get_open_goal (juc,n) =
  let g = get_pj (juc,n) in
  if g.pj_rule <> None then raise (NotAnOpenGoal (Some n));
  g

let upd_juc_map juc n pj =
  { juc with juc_map = Mint.add n pj juc.juc_map }

let upd_done juc =
  let is_done juc = function
    | RA_node n ->
        begin match (get_pj (juc,n)).pj_rule with
        | Some(true, _) -> true
        | _ -> false
        end
    | _ -> true in
  let rec upd juc n =
    let pj = get_pj (juc, n) in
    match pj.pj_rule with
    | None | Some(true, _) -> juc
    | Some(false,r) ->
        let juc = List.fold_left upd_arg juc r.pr_hyps in
        if List.for_all (is_done juc) r.pr_hyps then
          upd_juc_map juc n { pj with pj_rule = Some(true,r)}
        else juc
  and upd_arg juc = function
    | RA_node n -> upd juc n
    | _ -> juc in
  upd juc 0

let find_all_goals juc =
  let juc = upd_done juc in
  let rec aux ns n =
    let pj = get_pj (juc, n) in
    match pj.pj_rule with
    | None -> n :: ns
    | Some(d, r) -> if d then ns else List.fold_left auxa ns r.pr_hyps
  and auxa ns = function
    | RA_node n -> aux ns n
    | _ -> ns in
  juc, List.rev (aux [] 0)

let close_juc juc =
  let rec close n =
    let pj = get_pj (juc,n) in
    let hyps,concl = pj.pj_decl in
    let rule =
      match pj.pj_rule with
      | None -> raise (StillOpenGoal n)
      | Some (_,r) ->
        { pr_name = r.pr_name;
          pr_hyps = List.map close_arg r.pr_hyps } in
    { j_decl = LDecl.tohyps hyps, concl;
      j_rule = rule }
  and close_arg = function
    | RA_form f -> RA_form f
    | RA_id id  -> RA_id id
    | RA_mp mp  -> RA_mp mp
    | RA_node n -> RA_node (close n) in
  close 0

let upd_rule d pr (juc,n as g) =
  let pj = get_open_goal g in
  let sg = List.pmap (function RA_node n -> Some n | _ -> None) pr.pr_hyps in
  upd_juc_map juc n { pj with pj_rule = Some(d, pr) }, sg


let upd_rule_done = upd_rule true
let upd_rule      = upd_rule false

(* -------------------------------------------------------------------- *)
let rec destruct_product hyps fp =
  let module R = EcReduction in

  match EcFol.sform_of_form fp with
  | SFquant (Lforall, (x, t), f) -> Some (`Forall (x, t, f))
  | SFimp (f1, f2) -> Some (`Imp (f1, f2))
  | _ -> begin
    match R.h_red_opt R.full_red hyps fp with
    | None   -> None
    | Some f -> destruct_product hyps f
  end

(* -------------------------------------------------------------------- *)
let t_id msg (juc,n) =
  msg |> oiter (fun x -> Printf.fprintf stderr "DEBUG: %s\n%!" x);
  (juc, [n])

let t_on_goals t (juc,ln) =
  let juc,ln =
    List.fold_left (fun (juc,ln) n ->
      let juc,ln' = t (juc,n) in
      juc,List.rev_append ln' ln) (juc,[]) ln in
  juc,List.rev ln

let t_seq t1 t2 g = t_on_goals t2 (t1 g)

let rec t_lseq lt =
  match lt with
  | [] -> t_id None
  | t1::lt -> t_seq t1 (t_lseq lt)

let t_subgoal lt (juc,ln) =
  let len1 = List.length lt in
  let len2 = List.length ln in
  if len1 <> len2 then raise (InvalidNumberOfTactic(len2, len1));
  let juc, ln =
    List.fold_left2 (fun (juc,ln) t n ->
      let juc, ln' = t (juc, n) in
      juc, List.rev_append ln' ln) (juc,[]) lt ln in
  juc, List.rev ln

let t_on_firsts t i (juc, ln) =
  let (ln1, ln2) = List.take_n i ln in
  snd_map (List.append^~ ln2) (t_on_goals t (juc, ln1))

let t_on_lasts t i (juc, ln) =
  let (ln1, ln2) = List.take_n (max 0 (List.length ln - i)) ln in
  snd_map (List.append ln1) (t_on_goals t (juc, ln2))

let t_on_first t g = t_on_firsts t 1 g
let t_on_last  t g = t_on_lasts  t 1 g

let t_focus ((from_, to_), mode) t gs =
  let ngoals = List.length (snd gs) in

  if ngoals > 0 then
    let of_neg_idx i = if i < 0 then max 0 (ngoals + i) else i in
    let from_ = clamp 1 ngoals (of_neg_idx (odfl 1      from_)) in
    let to_   = clamp 1 ngoals (of_neg_idx (odfl ngoals to_  )) in
    let tx    =
      List.init ngoals
        (fun i ->
          let i = i + 1 in
          match mode with
          | `Include when i >= from_ && i <= to_ -> t
          | `Exclude when i <  from_ || i >  to_ -> t
          | _ -> t_id None)
    in
      t_subgoal tx gs
  else
    gs

let t_seq_subgoal t lt g = t_subgoal lt (t g)

let t_try_base t g =
  let rec is_user_error = function
    | EcTyping.TyError  _ -> true
    | TacError (true, _)  -> true
    | LocError (_, e)     -> is_user_error e
    | _ -> false
  in
  try `Success (t g)
  with e when is_user_error e -> `Failure e

let t_fail _g = tacuerror "FAIL TACTIC"

let t_try t g =
  match t_try_base t g with
  | `Failure _ -> t_id None g
  | `Success g -> g

let t_or t1 t2 g =
  match t_try_base t1 g with
  | `Failure _ -> t2 g
  | `Success g -> g

let rec t_lor lt g =
  match lt with
  | []       -> t_fail g
  | [t1]     -> t1 g
  | t1 :: lt -> t_or t1 (t_lor lt) g

let t_do b omax t g =
  let max = max (odfl max_int omax) 0 in

  let rec doit i g =
    let r = if i < max then Some (t_try_base t g) else None in

    match r with
    | None -> t_id None g

    | Some (`Failure e) ->
        let fail =
          match b, omax with
          | `Maybe, _      -> false
          | `All  , None   -> i < 1
          | `All  , Some m -> i < m
        in
          if fail then raise e else t_id None g

    | Some (`Success (juc, ln)) ->
        if   ln = [snd g]
        then (juc, ln)
        else t_subgoal (List.map (fun _ -> doit (i+1)) ln) (juc, ln)

  in
    doit 0 g

let t_repeat t = t_do `Maybe None t

let t_close t g =
  match t g with
  | (juc, []    ) -> (juc, [])
  | (_  , i :: _) -> raise (StillOpenGoal i)

let t_rotate mode sz (juc, ns) =
  let mrev = match mode with `Left -> identity | `Right -> List.rev in
  let sz   = if ns = [] then 0 else (max 0 sz) mod List.length ns in

  let (hd, tl) = List.take_n sz (mrev ns) in (juc, mrev (tl @ hd))

(* -------------------------------------------------------------------- *)
let get_node  g = (get_pj g).pj_decl
let get_goal  g = (get_open_goal g).pj_decl

let get_goal_e g =
  let hyps, concl = get_goal g in
  LDecl.toenv hyps, hyps, concl

let get_hyps  g = fst (get_goal g)
let get_concl g = snd (get_goal g)

(* -------------------------------------------------------------------- *)
let prove_goal_by sub_gs rule (juc,n as g) =
  let hyps,_ = get_goal g in
  let add_sgoal (juc,ns) sg =
    let juc,n = new_goal juc (hyps,sg) in juc, RA_node n::ns
  in
  let juc,ns = List.fold_left add_sgoal (juc,[]) sub_gs in
  let rule = { pr_name = rule ; pr_hyps = List.rev ns} in
  upd_rule rule (juc,n)

(* -------------------------------------------------------------------- *)
let tacerror = EcBaseLogic.tacerror

let tacuerror = EcBaseLogic.tacuerror

let cannot_apply s1 s2 =
  tacuerror "Can not apply %s tactic:@\n %s" s1 s2

(* -------------------------------------------------------------------- *)
let t_subgoal lt gs =
  try
    t_subgoal lt gs
  with
  | InvalidNumberOfTactic (i1, i2) ->
      tacerror (InvalNumOfTactic (i1, i2))

let t_admit g =
  let rule = { pr_name = RN_admit; pr_hyps = []; } in
    upd_rule_done rule g

let check_hyps hyps1 hyps2 =
  assert (hyps1 == hyps2) (* FIXME error message *)

(* WARNING this do not generate the subgoal included in n.
   It could be greater to ensure we have no circular dependency *)
(* Use the proof of n1 to prove n *)
let use_node juc n n1 =
  let hyps,concl = get_node (juc,n) in
  let hyps',concl' = get_goal (juc,n1) in
  check_hyps hyps hyps';
  check_conv hyps concl concl';
  let rule = { pr_name = RN_conv; pr_hyps = [RA_node n] } in
  fst (upd_rule rule (juc,n1))

let t_use n gs (juc,n1) =
  use_node juc n n1, gs

let t_change f (juc,n1) =
  let hyps = get_hyps (juc,n1) in
  EqTest.for_type_exn (LDecl.toenv hyps) f.f_ty tbool;
  let (juc,n) = new_goal juc (hyps,f) in
  t_use n [n] (juc,n1)

let t_red g =
  let hyps, concl = get_goal g in
  let f = EcReduction.h_red EcReduction.full_red hyps concl in
  t_change f g

let t_simplify ri (juc,n1 as g) =
  let hyps, concl = get_goal g in
  let f = EcReduction.simplify ri hyps concl in
  let (juc,n) = new_goal juc (hyps,f) in
  let rule = { pr_name = RN_conv; pr_hyps = [RA_node n] } in
  upd_rule rule (juc,n1)

let t_simplify_nodelta g = t_simplify nodelta g

let mkn_hyp juc hyps id =
  let f = LDecl.lookup_hyp_by_id id hyps in
  let juc,n = new_goal juc (hyps,f) in
  let rule = { pr_name = RN_hypothesis id; pr_hyps = [] } in
  let juc, _ = upd_rule_done rule (juc,n) in
  juc, n

let t_hyp id (juc,n as g) =
  let hyps = get_hyps g in
  let juc,nh = mkn_hyp juc hyps id in
  use_node juc nh n, []

let mkn_glob juc hyps p tys =
  let f = Ax.instanciate p tys (LDecl.toenv hyps) in
  let juc, n = new_goal juc (hyps,f) in
  let rule = { pr_name = RN_lemma (p, tys); pr_hyps = [] } in
  let juc, _ = upd_rule_done rule (juc, n) in
  juc, n

let t_glob p tys (juc,n as g) =
  let hyps = get_hyps g in
  let juc, nh = mkn_glob juc hyps p tys in
  use_node juc nh n, []

let t_smt ~strict hints pi g =
  let error = tacuerror ~catchable:(not strict) in

  let _,concl as goal = get_goal g in
  match concl.f_node with
    | FequivF   _  | FequivS   _
    | FhoareF   _  | FhoareS   _
    | FbdHoareF _  | FbdHoareS _ ->
      tacuerror "Cannot process program judgement, use skip tactic first"

    | _ ->
        try
          if EcEnv.check_goal hints pi goal then
            let rule = { pr_name = RN_smt; pr_hyps = [] } in
            upd_rule_done rule g
          else error "cannot prove goal"
        with EcWhy3.CannotTranslate _ ->
          error "cannot prove goal"

let t_clear_set ids (juc,n as g) =
  let pp_id fmt id = Format.fprintf fmt "%s" (EcIdent.name id) in
  let hyps,concl = get_goal g in
  if not (Mid.set_disjoint ids concl.f_fv) then begin
    let elts = Sid.elements (Mid.set_inter ids concl.f_fv) in
    tacuerror
      "Cannot clear %a, %s used in the conclusion"
      (EcPrinting.pp_list ",@ " pp_id) elts
      (if List.length elts = 1 then "it is" else "they are")
  end;
  let hyps =
    try LDecl.clear ids hyps
    with LDecl.Ldecl_error (LDecl.CanNotClear(id1,id2)) ->
      tacuerror "Cannot clear %a it is used in %a"
        pp_id id1 pp_id id2 in
  let juc,n1 = new_goal juc (hyps,concl) in
  let rule = { pr_name = RN_weak ids; pr_hyps = [RA_node n1] } in
  upd_rule rule (juc,n)

let t_clears ids g = t_clear_set (EcIdent.Sid.of_list ids) g
let t_clear id g = t_clear_set (EcIdent.Sid.singleton id) g

let gen_check_restr env pp_a a use restr =
  let restr = NormMp.norm_restr env restr in
  let ppe = EcPrinting.PPEnv.ofenv env in
  let pp_mp = EcPrinting.pp_topmod ppe in

  let check_xp xp _ =
    (* We check that the variable is not a variable in restr *)
    if NormMp.use_mem_xp xp restr then
      tacuerror "%a should not use the variable %a."
       (pp_a ppe) a (EcPrinting.pp_pv ppe) (pv_glob xp);
    (* We check that the variable is in the restriction of the abstract module
       in restr *)
    let check id2 =
      let mp2 = EcPath.mident id2 in
      let r2  = NormMp.get_restr env mp2 in
      if not (NormMp.use_mem_xp xp r2) then
        tacuerror "%a uses the variable %a, but should not use the module %a (which can use %a)" (pp_a ppe) a (EcPrinting.pp_pv ppe) (pv_glob xp)
          pp_mp mp2 (EcPrinting.pp_pv ppe) (pv_glob xp) in
    EcIdent.Sid.iter check restr.us_gl in
  EcPath.Mx.iter check_xp (use.us_pv);
  let check_gl id =
    let mp1 = EcPath.mident id in
    if NormMp.use_mem_gl mp1 restr then
      tacuerror "%a should not use the module %a."
        (pp_a ppe) a pp_mp mp1
    else
      let r1 = NormMp.get_restr env mp1 in
      let check_v xp2 _ =
        if not (NormMp.use_mem_xp xp2 r1) then
          tacuerror "%a should not be able to use %a (add restriction %a to %a)"
           (pp_a ppe) a
           (EcPrinting.pp_pv ppe) (pv_glob xp2)
           pp_mp xp2.x_top pp_mp mp1  in
      Mx.iter check_v restr.us_pv;

      let check_g id2 =
        let mp2 = EcPath.mident id2 in
        if not (NormMp.use_mem_gl mp2 r1) then
          let r2 = NormMp.get_restr env mp2 in
          if not (NormMp.use_mem_gl mp1 r2) then
            tacuerror
              "%a should not use %a; add restriction %a to %a or %a to %a"
            (pp_a ppe) a pp_mp mp1 pp_mp mp2
            pp_mp mp1 pp_mp mp2 pp_mp mp2 pp_mp mp1 in
      EcIdent.Sid.iter check_g restr.us_gl in
  EcIdent.Sid.iter check_gl use.us_gl

let check_restr env mp restr =
  let use = NormMp.mod_use env mp in
  let pp_mod ppe fmt mp =
    Format.fprintf fmt "The module %a" (EcPrinting.pp_topmod ppe) mp in
  gen_check_restr env pp_mod mp use restr

let check_modtype_restr env mp mt i restr =
  begin
    try EcTyping.check_sig_mt_cnv env mt i
    with EcTyping.TymodCnvFailure e ->
      let ppe = EcPrinting.PPEnv.ofenv env in
      tacuerror "%a : %a"
        (EcPrinting.pp_topmod ppe) mp
        (fun fmt e -> EcTyping.pp_cnv_failure fmt env e) e
  end;
  check_restr env mp restr

type app_arg =
  | AAform of form
  | AAmem  of EcIdent.t
  | AAmp   of EcPath.mpath * EcModules.module_sig
  | AAnode

type 'a app_arg_cb = LDecl.hyps -> gty option -> 'a -> app_arg

let check_arg do_arg hyps s x gty a =
  let gty = Fsubst.gty_subst s gty in
  let a = do_arg hyps (Some gty) a in
  match gty, a with
  | GTty ty  , AAform f ->
      EqTest.for_type_exn (LDecl.toenv hyps) ty f.f_ty; (* FIXME error message *)
      Fsubst.f_bind_local s x f, RA_form f
  | GTmem _   , AAmem m ->
      Fsubst.f_bind_mem s x m, RA_id m
  | GTmodty (emt, restr), AAmp (mp, mt)  ->
    let env = (LDecl.toenv hyps) in
    check_modtype_restr env mp mt emt restr;
    EcPV.check_module_in env mp emt;
    Fsubst.f_bind_mod s x mp, RA_mp mp
  | _ -> assert false (* FIXME error message *)

let mkn_apply do_arg (juc,n) args =
  if args = [] then (juc,n), []
  else
    let hyps,concl = get_node (juc,n) in
    let check_arg = check_arg do_arg hyps in
    let rec check_apply juc s ras f args =
      match args with
      | [] -> juc, List.rev ras, Fsubst.f_subst s f
      | a :: args' ->
        if is_forall f then
          let (x,gty,f') = destr_forall1 f in
          let s, ra = check_arg s x gty a in
          check_apply juc s (ra::ras) f' args'
        else if is_imp f then
          let (f1,f2) = destr_imp f in
          let a = do_arg hyps None a in
          assert (a = AAnode); (* FIXME error message *)
          let juc, n = new_goal juc (hyps, Fsubst.f_subst s f1) in
          check_apply juc s (RA_node n :: ras) f2 args'
        else
          let f = Fsubst.f_subst s f in
          match h_red_opt full_red hyps f with
          | None -> tacerror TooManyArguments
          | Some f -> check_apply juc Fsubst.f_subst_id ras f args in
    let juc, ras, concl = check_apply juc Fsubst.f_subst_id [] concl args in
    let (juc,n1) = new_goal juc (hyps,concl) in
    let rule = { pr_name = RN_apply; pr_hyps = RA_node n :: ras} in
    let juc, _ = upd_rule rule (juc,n1) in
    let ns = List.pmap (function RA_node n -> Some n | _ -> None) ras in
    (juc,n1), ns

let gen_t_apply do_arg fn args (juc,n) =
  let (juc,an), ns = mkn_apply do_arg (juc,fn) args in
  (use_node juc an n, ns)

let gen_t_apply_form do_arg f args (juc, n as g) =
  let hyps = get_hyps g in
  let juc, fn = new_goal juc (hyps, f) in
  let juc,ns = gen_t_apply do_arg fn args (juc,n) in
  juc, fn::ns

let t_apply_form = gen_t_apply_form (fun _ _ a -> a)

let gen_t_apply_glob do_arg p tys args (juc,n as g) =
  let hyps = get_hyps g in
  let juc,fn = mkn_glob juc hyps p tys in
  gen_t_apply do_arg fn args (juc,n)

let t_apply_glob = gen_t_apply_glob (fun _ _ a -> a)

let gen_t_apply_hyp do_arg id args (juc ,n as g) =
  let hyps = get_hyps g in
  let juc,fn = mkn_hyp juc hyps id in
  gen_t_apply do_arg fn args (juc, n)

let t_apply_hyp = gen_t_apply_hyp (fun _ _ a -> a)

let check_logic env p =
  try  ignore (EcEnv.Ax.by_path p env)
  with EcEnv.LookupFailure _ -> assert false

let t_apply_logic p tyargs args g =
  check_logic (LDecl.toenv (get_hyps g)) p;
  t_apply_glob p tyargs args g

let pattern_form name hyps f1 f =
  let x = EcIdent.create (odfl "x" name) in
  let fx = EcFol.f_local x f1.f_ty in
  let body =
    Hf.memo_rec 107
      (fun aux f ->
        if EcReduction.is_alpha_eq hyps f f1 then fx
        else f_map (fun ty -> ty) aux f)
      f in
  x, body

type dofpattern = LDecl.hyps -> form -> form -> (EcIdent.t * form)

let t_rewrite_gen fpat side f g =
  let side = match side with `LtoR -> true | `RtoL -> false in
  let hyps,concl = get_goal g in
  let env = LDecl.toenv hyps in
  let rec find_rewrite f =
    if is_eq f then destr_eq f, `Eq
    else if is_iff f then destr_iff f, `Eqv
    else
      match h_red_opt full_red hyps f with
      | Some f -> find_rewrite f
      | None   -> begin
        if side && (EqTest.for_type env f.f_ty EcTypes.tbool)
        then ((f, f_true), `Bool)
        else
          let ppe = EcPrinting.PPEnv.ofenv (LDecl.toenv hyps) in
            tacuerror "Do not known how to rewrite %a" (EcPrinting.pp_form ppe) f
      end in
  let (f1,f2),mode = find_rewrite f in
  let p,tys, fs =
    match mode with
    | `Eq   -> (if side then p_rewrite_l else p_rewrite_r), [f1.f_ty], [AAform f1; AAform f2]
    | `Eqv  -> (if side then p_rewrite_iff_l else p_rewrite_iff_r), [], [AAform f1; AAform f2]
    | `Bool -> p_rewrite_bool, [], [AAform f1] in
  let pred =
    let f = if side then f1 else f2 in
    let x, body = fpat hyps f concl in
    f_lambda [x,GTty f.f_ty] body in
  t_on_last
    t_red
    (t_apply_logic p tys (fs @ [AAform pred;AAnode;AAnode]) g)

let t_rewrite = t_rewrite_gen (pattern_form None)

let t_rewrite_node ?(fpat = pattern_form None) ((juc,an), gs) side n =
  let (_,f) = get_node (juc, an) in
  t_seq_subgoal (t_rewrite_gen fpat side f) [t_use an gs;t_id None] (juc,n)

let t_rewrite_hyp ?fpat side id args (juc,n as g) =
  let g = mkn_hyp juc (get_hyps g) id in
  t_rewrite_node ?fpat (mkn_apply (fun _ _ a -> a) g args) side n

let t_rewrite_glob ?fpat side p tys args (juc,n as g) =
  let g = mkn_glob juc (get_hyps g) p tys in
  t_rewrite_node ?fpat (mkn_apply (fun _ _ a -> a) g args) side n

let t_rewrite_form ?fpat side fp args (juc, n as g) =
  let (juc, fn) = new_goal juc (get_hyps g, fp) in
  let g = mkn_apply (fun _ _ a -> a) (juc, fn) args in
  let g = t_rewrite_node ?fpat g side n
  in
    snd_map (fun ns -> fn :: ns) g

let t_cut f g =
  let concl = get_concl g in
  t_apply_logic p_cut_lemma [] [AAform f;AAform concl;AAnode;AAnode] g

let t_intros ids (juc,n as g) =
  let hyps, concl = get_goal g in
  let add_local s id x gty =
    let id   = id.pl_desc in
    let name = EcIdent.name id in
    let gty  = Fsubst.gty_subst s gty in
    match gty with
    | GTty ty ->
        if name <> "_" && not (EcIo.is_sym_ident name) then
          tacerror (InvalidName name);
        LD_var(ty,None), Fsubst.f_bind_local s x (f_local id ty)

    | GTmem me ->
        if name <> "_" && not (EcIo.is_mem_ident name) then
          tacerror (InvalidName name);
        LD_mem me, Fsubst.f_bind_mem s x id

    | GTmodty (i,r) ->
        if name <> "_" && not (EcIo.is_mod_ident name) then
          tacerror (InvalidName name);
        LD_modty (i,r), Fsubst.f_bind_mod s x (EcPath.mident id)
  in

  let add_ld id ld hyps =
    set_loc id.pl_loc (LDecl.add_local id.pl_desc ld) hyps in

  let rec check_intros hyps ids s concl =
    match ids with
    | [] -> hyps, Fsubst.f_subst s concl
    | id::ids' ->
      if is_forall concl then
        let (x,gty,concl) = destr_forall1 concl in
        let ld, s = add_local s id x gty in
        let hyps = add_ld id ld hyps in
        check_intros hyps ids' s concl
      else if is_imp concl then
        let f1, f2 = destr_imp concl in
        let hyps = add_ld id (LD_hyp (Fsubst.f_subst s f1)) hyps in
        check_intros hyps ids' s f2
      else if is_let concl then
        let x,ty,e1,concl = destr_let1 concl in
        let s = Fsubst.f_bind_local s x (f_local id.pl_desc ty) in
        let hyps = add_ld id (LD_var (s.fs_ty ty, Some (Fsubst.f_subst s e1))) hyps in
        check_intros hyps ids' s concl
      else if s == Fsubst.f_subst_id then
        match h_red_opt full_red hyps concl with
        | None ->
          let ppe = EcPrinting.PPEnv.ofenv (LDecl.toenv hyps) in
          tacuerror "Do not known what to introduce in %a"
            (EcPrinting.pp_form ppe) concl
        | Some concl -> check_intros hyps ids s concl
      else check_intros hyps ids Fsubst.f_subst_id (Fsubst.f_subst s concl) in
  let hyps, concl = check_intros hyps ids Fsubst.f_subst_id concl in
  let (juc,n1) = new_goal juc (hyps,concl) in
  let ids = List.map unloc ids in
  let rule = { pr_name = RN_intro (`Raw ids); pr_hyps = [RA_node n1]} in
  upd_rule rule (juc,n)

(* internal use *)
let t_intros_i ids g =
  t_intros
    (List.map (fun id -> {pl_desc = id; pl_loc = EcLocation._dummy}) ids)
    g

let t_intros_1 ids g =
  match t_intros_i ids g with
  | (juc, [n]) -> (juc, n)
  | _ -> assert false

let createVarForBinding t =
  let v = EcIdent.create "_" in ((v, GTty t),EcFol.f_local v t)

let createVarForLet t =
  let v = EcIdent.create "_" in ((v, t), EcFol.f_local v t)

let t_reflex ?(reduce = false) g =
  let hyps = get_hyps g in

  let rec doit goal =
    let notaneq () = tacuerror "reflexivity: not an equality" in
      match EcFol.sform_of_form goal with
      | SFeq (f, _) ->
          t_apply_logic p_eq_refl [f.f_ty] [AAform f] g
      | _ when reduce -> begin
        match EcReduction.h_red_opt EcReduction.full_red hyps goal with
        | None      -> notaneq ()
        | Some goal -> doit goal
      end
      | _ -> notaneq ()
  in
    doit (get_concl g)

let t_transitivity f g =
  let concl = get_concl g in
  let (f1, f2) = destr_eq concl in
    t_apply_logic
      p_eq_trans [f.f_ty]
      (  (List.map (fun f -> AAform f) [f1; f; f2])
       @ (List.create 2 AAnode))
      g

let t_symmetry g =
  let concl = get_concl g in
  let (f1,f2) = destr_eq concl in
  t_rewrite_glob `LtoR EcCoreLib.p_eq_sym [f1.f_ty] [AAform f1;AAform f2] g

let t_true g =
  try  t_apply_logic p_true_intro [] [] g
  with _ -> tacuerror "goal is not conversion to true"

(* Use to create two set of vars of a list of types*)
let parseType create types =
  let parse ty =
    let (bX, fX) = create ty in
    let (bY, fY) = create ty in
    ((bX, bY), (fX, fY))
  in
  List.split (List.map parse types)

(* Generate a lemma that permits to elim a tuple *)
let gen_eq_tuple_elim_lemma types =
  let (bC, fC) = createVarForBinding EcTypes.tbool in
  let (vars, fvars) = parseType createVarForBinding types in
  let (varsx, varsy) = List.split vars in
  let (fvarsx, fvarsy) = List.split fvars in
  let bindings = varsx@varsy@[bC] in
  let hyp1 = f_eq (f_tuple fvarsx) (f_tuple fvarsy) in
  let hyp2 = f_imps (List.map (fun (x,y) -> f_eq x y) fvars) fC in
  f_forall bindings (f_imps [hyp2; hyp1] fC)

(* Generate a proof of eq_tuple_elim *)
let gen_eq_tuple_elim_proof types =
  let pred rvars fc =
    let (bT, fT) = createVarForBinding (ttuple types) in
    let projs = List.map createVarForLet types in
    let (intro, fprojs) = List.split projs in
    let introTu = LTuple intro in
    let eqs = List.map (fun (x, y) -> f_eq x y) (List.combine fprojs rvars) in
    let body = f_imps eqs fc in
    f_app (f_lambda [bT] (f_let introTu fT body)) [f_tuple rvars] (EcTypes.tbool)
  in

  let (locVars, locVarsF) = parseType createVarForLet types in
  let (locC, locCF) = createVarForLet EcTypes.tbool in
  let introVars =
    let (a, b) = List.split locVars in
    fst (List.split (a@b@[locC]))
  in
  let (_, rvars) = List.split locVarsF in
  let h1 = EcIdent.create "_" in
  let h2 = EcIdent.create "_" in
  let intro = t_intros_i (introVars@[h2;h1]) in
  t_lseq [
    intro;
    t_seq_subgoal
      (t_apply_form (pred rvars locCF) (List.map (fun _ -> AAnode) types))
      ((
        t_lseq [t_rewrite_hyp `RtoL h1 [];
        t_apply_hyp h2 [];
        t_true]
      )::(List.map (fun _ -> t_reflex ~reduce:false) types))
  ]

(* Generate a lemma that permits to split tuple *)
let gen_split_tuple_lemma types =
  let (vars, fvars) = parseType createVarForBinding types in
  let (varsx, varsy) = List.split vars in
  let (fvarsx, fvarsy) = List.split fvars in
  let bindings = varsx@varsy in
  let hyps = List.map (fun (x,y) -> f_eq x y) fvars in
  let body = f_eq (f_tuple fvarsx) (f_tuple fvarsy) in
  f_forall bindings (f_imps hyps body)

(* Generate the proof for gen_split_tuple_proof *)
let gen_split_tuple_proof types =
  let introVars = List.map (fun _ -> EcIdent.create "_") (types@types) in
  let introHyps = List.map (fun _ -> EcIdent.create "_") types in
  let rews = List.map (fun h -> t_rewrite_hyp `RtoL h []) introHyps in
  t_seq (t_lseq ((t_intros_i (introVars@introHyps))::rews)) t_reflex

(* -------------------------------------------------------------------- *)
exception NoMatchForTactic

let t_lazy_match fail tx g =
  try  tx (get_concl g) g
  with NoMatchForTactic ->
    let hyps, concl = get_goal g in
      match h_red_opt full_red hyps concl with
      | None    -> fail ()
      | Some cl -> tx cl g

(* -------------------------------------------------------------------- *)
module Logic : sig
  val or_intro : [`Left|`Right] -> bool -> EcPath.path

  val and_elim   : bool -> EcPath.path
  val or_elim    : bool -> EcPath.path
  val if_elim    : EcPath.path
  val iff_elim   : EcPath.path
  val false_elim : EcPath.path
end = struct
  let or_intro side ora =
    match side, ora with
    | `Left , true  -> p_ora_intro_l
    | `Right, true  -> p_ora_intro_r
    | `Left , false -> p_or_intro_l
    | `Right, false -> p_or_intro_r

  let and_elim = function
    | true  -> p_anda_elim
    | false -> p_and_elim

  let or_elim = function
    | true  -> p_ora_elim
    | false -> p_or_elim

  let if_elim  = p_if_elim
  let iff_elim = p_iff_elim

  let false_elim = p_false_elim
end

(* -------------------------------------------------------------------- *)
let t_or_intro side g =
  let internal concl g =
    match sform_of_form concl with
    | SFor (ora, (left, right)) ->
        t_apply_logic
          (Logic.or_intro side ora) []
          [AAform left; AAform right; AAnode] g
    | _ -> raise NoMatchForTactic
  in

  let fail () = tacuerror "goal is not a disjunction" in
    t_lazy_match fail internal g

let t_left  = t_or_intro `Left
let t_right = t_or_intro `Right

(* -------------------------------------------------------------------- *)
let t_elim_r ?(tryreduce = true) txs goal =
  let error () = tacuerror "don't know what to eliminate" in

  match sform_of_form (get_concl goal) with
  | SFimp (f1, f2) ->
      let rec aux f1 =
        let sf1 = sform_of_form f1 in

        match
          List.pick (fun tx ->
              try  Some (tx (f1, sf1) f2 goal)
              with NoMatchForTactic -> None)
            txs
        with
        | Some gs -> gs
        | None    ->
            if not tryreduce then error ();
            match h_red_opt full_red (get_hyps goal) f1 with
            | None    -> error ()
            | Some f1 -> aux f1
      in
        aux f1

    | _ -> error ()

(* -------------------------------------------------------------------- *)
let t_elim_false_r ((_, sf) : form * sform) concl goal =
  match sf with
  | SFfalse ->
      let args = [AAform concl] in
        t_apply_logic Logic.false_elim [] args goal
  | _ -> raise NoMatchForTactic

let t_elim_false goal = t_elim_r [t_elim_false_r] goal

(* --------------------------------------------------------------------- *)
let t_elim_and_r ((_, sf) : form * sform) concl goal =
  match sf with
  | SFand (b, (a1, a2)) ->
      let args  = [AAform a1; AAform a2; AAform concl; AAnode] in
      let felim = Logic.and_elim b in
        t_apply_logic felim [] args goal
  | _ -> raise NoMatchForTactic

let t_elim_and goal = t_elim_r [t_elim_and_r] goal

(* --------------------------------------------------------------------- *)
let t_elim_or_r ((_, sf) : form * sform) concl goal =
  match sf with
  | SFor (b, (a1, a2)) ->
      let args  = [AAform a1; AAform a2; AAform concl; AAnode; AAnode] in
      let felim = Logic.or_elim b in
        t_apply_logic felim [] args goal
  | _ -> raise NoMatchForTactic

let t_elim_or goal = t_elim_r [t_elim_or_r] goal

(* --------------------------------------------------------------------- *)
let t_elim_iff_r ((_, sf) : form * sform) concl goal =
  match sf with
  | SFiff (a1, a2) ->
      let args  = [AAform a1; AAform a2; AAform concl; AAnode] in
      let felim = Logic.iff_elim in
        t_apply_logic felim [] args goal
  | _ -> raise NoMatchForTactic

let t_elim_iff goal = t_elim_r [t_elim_iff_r] goal

(* -------------------------------------------------------------------- *)
let t_elim_if_r ((_, sf) : form * sform) concl goal =
  match sf with
  | SFif (a1, a2, a3) ->
      let args =
        [AAform a1; AAform a2; AAform a3; AAform concl; AAnode; AAnode]
      in
        t_apply_logic Logic.if_elim [] args goal

  | _ -> raise NoMatchForTactic

let t_elim_if goal = t_elim_r [t_elim_if_r] goal

(* -------------------------------------------------------------------- *)
let t_elim_exists_r ((f, _) : form * sform) concl (juc, n) =
  match f.f_node with
  | Fquant (Lexists, bd, body) ->
      let (hyps, _ ) = get_goal (juc, n) in
      let (juc , n1) = new_goal juc (hyps, f_forall bd (f_imp body concl)) in
      let rule = { pr_name = RN_elim `Exist; pr_hyps = [RA_node n1] } in
        upd_rule rule (juc, n)

  | _ -> raise NoMatchForTactic

let t_elim_exists goal = t_elim_r [t_elim_exists_r] goal

(* -------------------------------------------------------------------- *)
let t_elim_eq_tuple_r ((_, sf) : form * sform) concl goal =
  match sf with
  | SFeq (a1, a2) when is_tuple a1 && is_tuple a2 ->
      let fs1 = destr_tuple a1 in
      let fs2 = destr_tuple a2 in
      let types = List.map (fun v -> v.f_ty) fs1 in
      let args = List.map (fun f -> AAform f) (fs1 @ fs2 @ [concl]) in
      let lemma = gen_eq_tuple_elim_lemma types in
      let proof = gen_eq_tuple_elim_proof types in
      let gs = t_apply_form lemma (args@[AAnode]) goal in
        t_on_first proof gs

  | _ -> raise NoMatchForTactic

let t_elim_eq_tuple goal = t_elim_r [t_elim_eq_tuple_r] goal

(* -------------------------------------------------------------------- *)
let t_elim_default_r = [
  t_elim_false_r;
  t_elim_and_r;
  t_elim_or_r;
  t_elim_iff_r;
  t_elim_if_r;
  t_elim_eq_tuple_r;
  t_elim_exists_r;
]

let t_elim goal =
  t_elim_r t_elim_default_r goal

(* -------------------------------------------------------------------- *)
let t_elim_hyp h g =
  let f = LDecl.lookup_hyp_by_id h (get_hyps g) in
    t_subgoal [t_hyp h; t_elim] (t_cut f g)

(* -------------------------------------------------------------------- *)
let t_generalize_form ?fpat name f g =
  let fpat =
    match fpat with
    | None -> pattern_form name
    | Some fpat -> fpat
  in
  let hyps,concl = get_goal g in
  let x,body = fpat hyps f concl in
  let ff = f_forall [x,GTty f.f_ty] body in
  t_apply_form ff [AAform f] g

let t_generalize_hyps ?(clear = false) ids g =
  let hyps,concl = get_goal g in
  let env1 = LDecl.toenv hyps in
  let rec aux (s:f_subst) ids =
    match ids with
    | [] -> Fsubst.f_subst s concl, [], []
    | id::ids ->
      match LDecl.ld_subst s (LDecl.lookup_by_id id hyps) with
      | LD_var (ty,body) ->
        let x = EcIdent.fresh id in
        let s = Fsubst.f_bind_local s id (f_local x ty) in
        let ff,args,lt = aux s ids in
        begin match body with
        | Some body when not clear ->
          f_let (LSymbol(x,ty)) body ff, args, lt
        | _ ->
          f_forall [x, GTty ty] ff, AAform (f_local id ty) :: args, lt
        end
      | LD_mem mt ->
        let x = EcIdent.fresh id in
        let s = Fsubst.f_bind_mem s id x in
        let ff, args, lt = aux s ids in
        f_forall [x, GTmem mt] ff, AAmem id :: args, lt
      | LD_modty (mt,r) ->
        let x = EcIdent.fresh id in
        let s = Fsubst.f_bind_mod s id (EcPath.mident x) in
        let mp = EcPath.mident id in
        let sig_ = (EcEnv.Mod.by_mpath mp env1).EcModules.me_sig in
        let ff, args, lt = aux s ids in
        f_forall [x,GTmodty(mt,r)] ff, AAmp(mp,sig_) :: args, lt
      | LD_hyp f ->
        let ff, args, lt = aux s ids in
        f_imp f ff, AAnode :: args, t_hyp id :: lt
      | LD_abs_st _ ->
        tacuerror "can not generalize abstract statement" in
  let ff, args, lt = aux Fsubst.f_subst_id ids in
  t_seq_subgoal (t_apply_form ff args) (t_id None :: lt) g

let t_generalize_hyp ?clear id g = t_generalize_hyps ?clear [id] g

let t_generalize_hyps clear ids g =
  if clear then
    t_seq (t_generalize_hyps ids) (t_clears ids) g
  else t_generalize_hyps ids g

(* -------------------------------------------------------------------- *)
let t_elimT_form tys p f sk g =
  let noelim () = tacuerror "not a valid elimination principle" in

  let (hyps, concl) = get_goal g in
  let env = LDecl.toenv hyps in
  let ax  = EcEnv.Ax.by_path p env in

  let rec skip i a f =
    match i, EcFol.sform_of_form f with
    | Some i, _ when i <= 0 -> (a, f)
    | Some i, SFimp (_, f2) -> skip (Some (i-1)) (AAnode :: a) f2
    | None  , SFimp (_, f2) -> skip None (AAnode :: a) f2
    | Some _, _ -> noelim ()
    | None  , _ -> (a, f)
  in

  match ax.EcDecl.ax_spec with
  | None -> noelim ()

  | Some _ ->
      let ax = EcEnv.Ax.instanciate p tys env in
      let (pr, prty, ax) =
        match sform_of_form ax with
        | SFquant (Lforall, (pr, GTty prty), ax) -> (pr, prty, ax)
        | _ -> noelim ()
      in

      if not (EqTest.for_type env prty (tfun f.f_ty tbool)) then
        noelim();

      let (aa1, ax) = skip None [] ax in

      let (x, _xty, ax) =
        match sform_of_form ax with
        | SFquant (Lforall, (x, GTty xty), ax) -> (x, xty, ax)
        | _ -> noelim ()
      in

      let (aa2, ax) =
        let rec doit ax aa =
          match destruct_product hyps ax with
          | Some (`Imp (f1, f2)) when Mid.mem pr f1.f_fv ->
              doit f2 (AAnode :: aa)
          | _ -> (aa, ax)
        in
          doit ax []
      in

      let pf =
        let (_, concl) = skip (Some sk) [] concl in
        let (z, concl) = pattern_form (Some (EcIdent.name x)) hyps f concl in
          Fsubst.f_subst_local pr (f_lambda [(z, GTty f.f_ty)] concl) ax
      in

      let pf_inst = Fsubst.f_subst_local x f pf in

      let (aa3, sk) =
        let rec doit pf_inst (aa, sk) =
          if   EcReduction.is_conv hyps pf_inst concl
          then (aa, sk)
          else
            match destruct_product hyps pf_inst with
            | Some (`Imp (_, f2)) -> doit f2 (AAnode :: aa, sk+1)
            | _ -> noelim ()
        in
          doit pf_inst ([], sk)
      in

      let pf = f_lambda [(x, GTty f.f_ty)] (snd (skip (Some sk) [] pf)) in
        t_apply_glob p tys (AAform pf :: aa1 @ (AAform f :: (aa2 @ aa3))) g

(* -------------------------------------------------------------------- *)
let t_elimT_ind mode goal =
  let error () = tacuerror "don't know what to eliminate" in

  let hyps = get_hyps goal in
  let env  = LDecl.toenv (get_hyps goal) in

  match sform_of_form (get_concl goal) with
  | SFquant (Lforall, (x, GTty ty), _) -> begin
      match EcEnv.Ty.scheme_of_ty mode ty env with
      | None -> error ()
      | Some (p, tys) ->
          let id = LDecl.fresh_id hyps (EcIdent.name x) in
            t_lseq
              [t_intros_i [id];
               t_elimT_form tys p (f_local id ty) 0;
               t_clear id;
               t_simplify EcReduction.beta_red]
              goal
    end

  | _ -> error ()

(* -------------------------------------------------------------------- *)
let t_case f g =
  t_elimT_form [] p_case_eq_bool f 0 g

let gen_t_exists do_arg fs (juc,n as g) =
  let hyps,concl = get_goal g in
  let rec aux s ras fs concl =
    match fs with
    | [] -> ras, (Fsubst.f_subst s concl)
    | f::fs' ->
      if is_exists concl then
        let (x,gty,concl) = destr_exists1 concl in
        let s, a = check_arg do_arg hyps s x gty f in
        aux s (a::ras) fs' concl
      else
        let concl = Fsubst.f_subst s concl in
        match h_red_opt full_red hyps concl with
        | Some f -> aux Fsubst.f_subst_id ras fs f
        | None ->
          let ppe = EcPrinting.PPEnv.ofenv (LDecl.toenv hyps) in
          tacuerror "Do not known how to split %a"
            (EcPrinting.pp_form ppe) concl in
  let args,concl = aux Fsubst.f_subst_id [] fs concl in
  let (juc,n1) = new_goal juc (hyps,concl) in
  let rule =
    {pr_name = RN_intro `Exist; pr_hyps = List.rev_append args [RA_node n1] } in
  upd_rule rule (juc,n)

let t_split g =
  let hyps, concl = get_goal g in
  let rec aux f =
    match f.f_node with
    | Fop(p,_) when EcPath.p_equal p p_true -> t_true g
    | Fapp({f_node = Fop(p,_)}, [f1;f2]) ->
      begin
        match op_kind p with
        | OK_and b ->
          t_apply_logic (if b then p_anda_intro else p_and_intro)
                [] [AAform f1;AAform f2;AAnode;AAnode] g
        | OK_iff   ->
          t_apply_logic p_iff_intro []
                [AAform f1;AAform f2;AAnode;AAnode] g
        | OK_eq when EcReduction.is_conv hyps f1 f2 -> t_reflex ~reduce:true g
        | OK_eq when is_tuple f1 && is_tuple f2 ->
          let fs1 = destr_tuple f1 in
          let fs2 = destr_tuple f2 in
          let types = List.map (fun v -> v.f_ty) fs1 in
          let args = List.map (fun f -> AAform f) (fs1@fs2) in
          let nodes = List.map (fun _ -> AAnode) fs1 in
          let lemma = gen_split_tuple_lemma types in
          let proof = gen_split_tuple_proof types in
          let gs = t_apply_form lemma (args@nodes) g in
          t_on_first proof gs
         | _ -> aux_red f
      end
    | Fif(f1,_f2,_f3) ->
      if f_equal concl f then t_case f1 g
      else t_seq (t_change f) (t_case f1) g
    | _ ->  aux_red f
  and aux_red f =
    match h_red_opt full_red hyps f with
    | Some f -> aux f
    | None ->
      let ppe = EcPrinting.PPEnv.ofenv (LDecl.toenv hyps) in
      tacuerror "Do not known how to split %a"
        (EcPrinting.pp_form ppe) f in
  aux concl

let t_subst_gen x h side g =
  let hyps,concl = get_goal g in
  let f = LDecl.lookup_hyp_by_id h hyps in
  let hhyps,_,_ =
    List.find_split (fun (id, _) -> EcIdent.id_equal x id) (LDecl.tohyps hyps).h_local in
  let rec gen fv hhyps =
    match hhyps with
    | [] -> ([], [], concl)
    | (id, lk) :: hhyps ->
      if EcIdent.id_equal id h then gen fv hhyps
      else match lk with
      | LD_var (ty, Some body) when not (Mid.set_disjoint body.f_fv fv) ->
        let aa, ids, concl = gen (Sid.add id fv) hhyps in
        aa, id::ids, f_let (LSymbol(id,ty)) body concl
      | LD_hyp f when not (Mid.set_disjoint f.f_fv fv) ->
        let aa, ids, concl = gen fv hhyps in
        id::aa, id::ids, f_imp f concl
      | _ -> gen fv hhyps in
  let aa, ids, concl = gen (Sid.singleton x) hhyps in
  let t_first =
    t_seq_subgoal (t_rewrite side f)
      [ t_hyp h;
        t_seq (t_clears (x::h::ids)) (t_intros_i ids) ] in
  t_seq_subgoal (t_apply_form concl (List.map (fun _ -> AAnode) aa))
    (t_first :: List.map t_hyp aa) g

let cansubst_eq hyps x f1 f2 =
  match f1.f_node, x with
  | Flocal id, Some x when EcIdent.id_equal id x ->
    let f2 = simplify {no_red with delta_h = None} hyps f2 in
    if Mid.mem id f2.f_fv then None else Some id
  | Flocal id, None ->
    let f2 = simplify {no_red with delta_h = None} hyps f2 in
    if Mid.mem id f2.f_fv then None else Some id
  | _         -> None

let is_subst_eq hyps x (hid,lk) =
  match lk with
  | LD_hyp f ->
    if is_eq_or_iff f then
      let f1, f2 = destr_eq_or_iff f in
      match cansubst_eq hyps x f1 f2 with
      | Some id -> Some(hid, id,`LtoR)
      | None ->
        match cansubst_eq hyps x f2 f1 with
        | Some id -> Some(hid, id,`RtoL)
        | None -> None
    else None
  | _ -> None

let t_subst1_loc x g =
  let hyps = get_hyps g in
  match List.pick (is_subst_eq hyps x) (LDecl.tohyps hyps).h_local with
  | None ->
    begin match x with
    | None -> tacuerror "Cannot find non recursive equation to substitute"
    | Some id ->
      tacuerror "Cannot find non recursive equation on %s" (EcIdent.name id)
    end
  | Some(h, x, side) ->
      t_subst_gen x h side g

(* Substitution of f1 = Fpvar(pv,m) | Fglob(mp,m) *)

let pattern_pv hyps f1 f =
  let x = EcIdent.create "x" in
  let fx = EcFol.f_local x f1.f_ty in
  let env = LDecl.toenv hyps in
  let f1 = EcEnv.NormMp.norm_form env f1 in
  let subst =
    match f1.f_node with
    | Fpvar (pv,m) -> EcPV.PVM.add env pv m fx EcPV.PVM.empty
    | Fglob (mp,m) -> EcPV.PVM.add_glob env mp m fx EcPV.PVM.empty
    | _ -> assert false in
  let body = EcPV.PVM.subst env subst f in
  x, body

let t_subst_pv_gen h side g =
  let hyps,_ = get_goal g in
  let f = LDecl.lookup_hyp_by_id h hyps in
  let to_gen =
    List.filter (fun (id,lk) ->
      match lk with
      | LD_hyp _ -> not (EcIdent.id_equal h id)
      | LD_var (_, Some _) -> true
      | _ -> false) (List.rev (LDecl.tohyps hyps).h_local) in
  let to_gen = List.map fst to_gen in
  let to_intros =
    List.map (fun id -> { pl_loc = EcLocation._dummy; pl_desc = id }) to_gen in
  t_seq (t_generalize_hyps true to_gen)
    (t_seq_subgoal (t_rewrite_gen pattern_pv side f)
            [t_hyp h;
             t_seq (t_clear h)
               (t_intros to_intros)]) g

let cansubst_pv_eq hyps fx f1 f2 =
  let env = (LDecl.toenv hyps) in
  let testfx f1 =
    match fx with
    | None -> true
    | Some fx -> f_equal f1 (EcEnv.NormMp.norm_form env fx) in
  let check f1 =
    let f1' = EcEnv.NormMp.norm_form env f1 in
    if testfx f1' then
      match f1'.f_node with
      | Fpvar(pv,m) ->
        let f2 = simplify {no_red with delta_h = None} hyps f2 in
        let fv = EcPV.PV.fv env m f2 in
        if EcPV.PV.mem_pv env pv fv then None
        else Some f1'
      | Fglob(mp,m) ->
        let f2 = simplify {no_red with delta_h = None} hyps f2 in
        let fv = EcPV.PV.fv env m f2 in
        if EcPV.PV.mem_glob env mp fv then None
        else Some f1'
      | _ -> None
    else None in
  match f1.f_node with
  | Fpvar _ | Fglob _ -> check f1
  | _ -> None

let is_subst_pv_eq hyps fx (hid,lk) =
  match lk with
  | LD_hyp f ->
    if is_eq_or_iff f then
      let f1, f2 = destr_eq_or_iff f in
      match cansubst_pv_eq hyps fx f1 f2 with
      | Some id -> Some(hid, id,`LtoR)
      | None ->
        match cansubst_pv_eq hyps fx f2 f1 with
        | Some id -> Some(hid, id,`RtoL)
        | None -> None
    else None
  | _ -> None

let t_subst1_pv fx g =
  let hyps = get_hyps g in
  let rec aux local =
    match local with
    | [] -> tacuerror "cannot find something to subst"
    | h :: l ->
      match is_subst_pv_eq hyps fx h with
      | Some(h, _x, side) ->
        (try t_subst_pv_gen h side g with EcPV.MemoryClash -> aux l)
      | _ -> aux l in
  aux (LDecl.tohyps hyps).h_local

let t_subst1_id h g =
  let hyps = get_hyps g in
  let k = LDecl.lookup_by_id h hyps in
  let error () = tacuerror "cannot find something to subst" in
  match is_subst_eq hyps None (h,k) with
  | Some(h,x,side) -> t_subst_gen x h side g
  | _ ->
    match is_subst_pv_eq hyps None (h,k) with
    | Some(h, _x, side) ->
      (try t_subst_pv_gen h side g with
        EcPV.MemoryClash -> error ())
    | _ -> error()

let t_subst1 fx g =
  match fx with
  | None -> t_or (t_subst1_loc None) (t_subst1_pv None) g
  | Some fx ->
    match fx.f_node with
    | Flocal id -> t_subst1_loc (Some id) g
    | (Fpvar _ | Fglob _) -> t_subst1_pv (Some fx) g
    | _ -> tacuerror "subst1"           (* FIXME: error message *)

let t_subst_all =
  t_repeat (t_subst1 None)

let gen_find_in_hyps eq f hyps =
  let test (_,lk) =
    match lk with
    | LD_hyp f' -> eq hyps f f'
    | _ -> false in
  fst (List.find test (LDecl.tohyps hyps).h_local)

let alpha_find_in_hyps f hyps =
  gen_find_in_hyps EcReduction.is_alpha_eq f hyps

let find_in_hyps f hyps =
  gen_find_in_hyps is_conv f hyps


let t_gen_assumption eq g =
  let (hyps,concl) = get_goal g in
  let h =
    try  gen_find_in_hyps eq concl hyps
    with Not_found -> tacuerror "no assumption"
  in
    t_hyp h g

let t_alpha_assumption g = t_gen_assumption EcReduction.is_alpha_eq g

let t_assumption g =
  t_or t_alpha_assumption (t_gen_assumption is_conv) g

let t_progress ?(split=true) tac g =
  let tac = t_or t_alpha_assumption tac in
  let rec aux g = t_seq t_simplify_nodelta aux0 g
  and aux0 g =
    t_seq (t_try tac) aux1 g
  and aux1 g =
    let hyps,concl = get_goal g in
    match concl.f_node with
    | Fquant(Lforall,bd,_) ->
      let ids =
        LDecl.fresh_ids hyps (List.map (fun (id,_) -> EcIdent.name id) bd) in
      t_seq (t_intros_i ids) aux0 g
    | Flet (LTuple fs,f1,_) ->
      let p = p_tuple_ind (List.length fs) in
      t_seq (t_elimT_form (List.map snd fs) p f1 0) aux0 g
    | Fapp({f_node = Fop(p,_)}, [f1;_]) when EcPath.p_equal p EcCoreLib.p_imp ->
      let id = LDecl.fresh_id hyps "H" in
      t_seq (t_intros_i [id]) (aux2 id f1) g
    | _ ->
      if not split then t_id None g else t_try (t_seq t_split aux0) g
  and aux2 id f g =
    let t1,aux =
      match f.f_node with
      | Fop(p,_) when EcPath.p_equal p p_false -> t_elim_hyp id, aux
      | Fapp({f_node = Fop(p,_)}, [_;_] ) when is_op_and p ->
        t_seq (t_elim_hyp id) (t_clear id),
        aux0
      | Fquant(Lexists,_,_) ->
        t_elim_hyp id, aux0
      | _ when is_eq f ->
        let f1, f2 = destr_eq f in
        if is_tuple f1 && is_tuple f2 then
          t_seq (t_elim_hyp id) (t_clear id), aux0
        else t_try (t_subst1_id id), aux

      | _ -> t_try (t_subst1_id id), aux in
    t_seq t1 aux g in
  aux g

(* -------------------------------------------------------------------- *)
let rec t_split_build g =
  let hyps, concl = get_goal g in
  let rec aux f =
    match f.f_node with
    | Fop(p,_) when EcPath.p_equal p p_true ->
      let id = EcIdent.create "_" in
      (id, f_true), t_true

    | Fapp({f_node = Fop(p,_)}, [f1;f2]) ->
      begin match op_kind p with
      | OK_and b ->
        let tac = t_apply_logic (if b then p_anda_intro else p_and_intro)
                [] [AAform f1;AAform f2;AAnode;AAnode] in
        let (juc, gs) = tac g in
        let (id1, ff1), tac1 = t_build (juc, List.nth gs 0) in
        let (id2, ff2), tac2 = t_build (juc, List.nth gs 1) in
        let ff = f_and ff1 ff2 in
        (id1, ff),
        t_lseq [
          t_elim_hyp id1;t_clear id1; t_intros_i [id1;id2];
          t_seq_subgoal tac [
            t_seq (t_clear id2) tac1;
            t_seq (t_clear id1) tac2]]

      | OK_eq when EcReduction.is_conv hyps f1 f2 ->
        let id = EcIdent.create "_" in
        (id, f_true), (t_reflex ~reduce:true)
      | OK_eq when is_tuple f1 && is_tuple f2 ->
          let fs1 = destr_tuple f1 in
          let fs2 = destr_tuple f2 in
          let types = List.map (fun v -> v.f_ty) fs1 in
          let args = List.map (fun f -> AAform f) (fs1@fs2) in
          let nodes = List.map (fun _ -> AAnode) fs1 in
          let lemma = gen_split_tuple_lemma types in
          let proof = gen_split_tuple_proof types in
          let (juc,gs) = t_apply_form lemma (args@nodes) g in
          let gs = List.tl gs in
          let idfts = List.map (fun n -> t_build0 (juc,n)) gs in
          let ids =
            List.fold_left (fun ids ((id,_),_) -> Sid.add id ids) Sid.empty idfts in
          let tacs =
            List.map (fun ((id,_), tac) ->
              t_seq (t_clear_set (Sid.remove id ids)) tac) idfts in
          let ff = f_ands (List.map (fun ((_,f), _) -> f) idfts) in
          let id = EcIdent.create "_" in
          let rec etacs ids =
            match ids with
            | [] -> assert false
            | [(idi,_),_] ->
              t_seq (t_generalize_hyps true [id]) (t_intros_i [idi])
            | ((idi,_),_) :: ids ->
              t_lseq [t_elim_hyp id; t_clear id;
                      t_intros_i [idi;id]; etacs ids] in
          (id, ff),
          t_seq (etacs idfts)
            (t_seq_subgoal
               (t_apply_form lemma (args@nodes))
               (proof :: tacs))

      | _ -> aux_red f
      end
    | Fif(f1,_f2,_f3) ->
      let same = f_equal f concl in
      let tac =
        if same then t_case f1
        else t_seq (t_change f) (t_case f1) in
      let (juc,gs) = tac g in
      let (id1, ff1), tac1 = t_build (juc, List.nth gs 0) in
      let (id2, ff2), tac2 = t_build (juc, List.nth gs 1) in
      let id = EcIdent.create "_" in
      let h1 = EcIdent.create "_" in
      let h2 = EcIdent.create "_" in
      let h3 = EcIdent.create "_" in
      let u1, tacu1 =
        if is_imp ff1 then
          let (f1',u1) = destr_imp ff1 in
          if f_equal f1 f1' then u1, t_intros_i [h3]
          else ff1, t_id None
        else ff1, t_id None in
      let u2, tacu2 =
        if is_imp ff2 then
          let (nf1',u2) = destr_imp ff2 in
          if f_equal (f_not f1) nf1' then u2, t_intros_i [h3]
          else ff2, t_id None
        else ff2, t_id None in
      let ff = f_if f1 u1 u2 in
      let tac =
        t_seq_subgoal
          (t_seq (t_generalize_hyps true [id])
             (if same then t_seq (t_change (f_imp ff f)) (t_case f1)
              else t_case f1))
          [(* hyps |- f1 => u1 => f2{f1<-true} *)
            t_seq_subgoal (t_seq (t_intros_i [h1;h2]) (t_cut ff1))
              [t_seq tacu1 (t_apply_hyp h2 []);
               t_lseq [t_intros_i [id1];t_generalize_hyp h1;
                       t_clears [h1; h2];
                       tac1]];
           (* hyps |- !f1 => u2 => f3{f1<-false} *)
            t_seq_subgoal (t_seq (t_intros_i [h1;h2]) (t_cut ff2))
              [t_seq tacu2 (t_apply_hyp h2 []);
               t_lseq [t_intros_i [id2];t_generalize_hyp h1;
                       t_clears [h1;h2];
                       tac2]] ] in
      (id, ff), tac

    | _ ->  aux_red f
  and aux_red f =
    match h_red_opt full_red hyps f with
    | Some f -> aux f
    | None ->
      let id = EcIdent.create "_" in
      (id,concl), t_hyp id in
  aux concl

and t_build g =
  let (juc,gs) = t_simplify_nodelta g in
  let idf,tac = t_build0 (juc, List.hd gs) in
  idf, t_seq t_simplify_nodelta tac

and t_build0 g =
  let hyps, concl = get_goal g in
  try
    let h = gen_find_in_hyps EcReduction.is_alpha_eq concl hyps in
    let id = EcIdent.create "_" in
    (id, f_true), t_hyp h
  with Not_found -> t_build1 g

and t_build1 g =
  let hyps, concl = get_goal g in
  match concl.f_node with
  | Fquant(Lforall,bd,_) ->
    let ids =
      LDecl.fresh_ids hyps (List.map (fun (id,_) -> EcIdent.name id) bd) in
    let (juc,gs) = t_intros_i ids g in
    let (id,u), tac = t_build1 (juc, List.hd gs) in
    let params = List.map2 (fun (_, x) id -> id, x) bd ids in
    let doarg (_, gty) id =
      match gty with
      | GTty ty -> AAform (f_local id ty)
      | GTmodty (mt,_mr) ->
        let sig_ = ModTy.sig_of_mt (LDecl.toenv hyps) mt in
        AAmp (EcPath.mident id, sig_)
      | GTmem _mt -> AAmem id in
    let args = List.map2 doarg bd ids in
    let ff = f_forall params u in
    let id' = EcIdent.create "_" in
    let tac'=
      t_seq (t_intros_i ids)
        (t_seq_subgoal (t_cut u)
           [t_apply_hyp id' args;
            t_lseq [t_clear id'; t_intros_i [id];
                    tac]]) in
    (id', ff), tac'

  | Flet (LTuple fs,f1,_) ->
    let p = p_tuple_ind (List.length fs) in
    let tac = t_elimT_form (List.map snd fs) p f1 0 in
    let (juc,gs) = tac g in
    let idf,tac1 = t_build0 (juc, List.hd gs) in
    idf, t_seq tac tac1

  | Fapp({f_node = Fop(p,_)}, [f1;_]) when EcPath.p_equal p EcCoreLib.p_imp ->
    t_build2 f1 g
  | _ ->
    t_split_build g

and t_build2 f1 g =
  match f1.f_node with
  | Fapp({f_node = Fop(p,_)}, [_;_] ) when is_op_and p ->
    let id = EcIdent.create "_" in
    let tac =
      t_lseq [t_intros_i [id]; t_elim_hyp id; t_clear id] in
    let (juc, gs) = tac g in
    let idf, tac1 = t_build0 (juc, List.hd gs) in
    idf, t_seq tac tac1
  | Fquant(Lexists,_,_) ->
    let id = EcIdent.create "_" in
    let tac =
      t_lseq [t_intros_i [id]; t_elim_hyp id] in
    let (juc, gs) = tac g in
    let (id1,ff1), tac1 = t_build0 (juc, List.hd gs) in
    (id1, ff1), t_seq tac tac1

  | _ when is_eq_or_iff f1 ->
    let f, f2 = destr_eq f1 in
    let id = EcIdent.create "_" in
    if is_tuple f && is_tuple f2 then
      let tac =
        t_lseq [t_intros_i [id]; t_elim_hyp id;t_clear id] in
      let (juc,gs) = tac g in
      let (idf,tac1) = t_build0 (juc, List.hd gs) in
      idf, t_seq tac tac1
    else
      let tac1 = t_intros_i [id] in
      let (juc,gs), tac2 =
        match t_try_base (t_seq tac1 (t_subst1_id id)) g with
        | `Failure _ -> tac1 g, t_id None
        | `Success g -> g, (t_subst1_id id) in
      let (id1,ff1), tac3 = t_build0 (juc, List.hd gs) in
      let ff = f_imp f1 ff1 in
      (id1, ff),
      t_seq tac1
        (t_seq_subgoal (t_cut ff1) [
          t_seq (t_apply_hyp id1 [AAnode]) (t_hyp id);
          t_lseq [tac2; t_try (t_clear id1);
                  t_intros_i [id1]; tac3]])
  | _ ->
    let id = EcIdent.create "_" in
    let tac = t_intros_i [id] in
    let (juc,gs) = tac g in
    let (id1,ff1), tac1 = t_build0 (juc, List.hd gs) in
    let ff = f_imp f1 ff1 in
    (id1, ff),
    t_seq tac
      (t_seq_subgoal (t_cut ff1) [
        t_seq (t_apply_hyp id1 [AAnode]) (t_hyp id);
        t_lseq [t_clear id1; t_intros_i [id1]; tac1]])

(* -------------------------------------------------------------------- *)
let t_progress_one g =
  let (id, f), tac = t_build g in
    t_seq_subgoal (t_cut f)
      [t_seq (t_intros_i [id]) tac;
       t_seq t_simplify_nodelta (t_try t_true)]
      g

(* -------------------------------------------------------------------- *)
let rec t_intros_elim n g =
  if n = 0 then t_id None g
  else
    let hyps, concl = get_goal g in
    match concl.f_node with
    | Fquant (Lforall, bd, _) ->
      let bd, n =
        let len = List.length bd in
        if len <= n then bd, n-len
        else List.take n bd, 0 in
      let ids =
        LDecl.fresh_ids hyps (List.map (fun (id,_) -> EcIdent.name id) bd) in
      t_seq (t_intros_i ids) (t_intros_elim n) g

    |  Fapp({f_node = Fop(p,_)}, [f;_]) when EcPath.p_equal p EcCoreLib.p_imp ->
      begin match f.f_node with
      | Fapp({f_node = Fop(p,_)}, [_;_] ) when is_op_and p ->
        t_seq t_elim_and (t_intros_elim (n+1)) g
      | Fquant(Lexists,bd,_) ->
        t_seq t_elim (t_intros_elim (n+List.length bd)) g
      | _ when is_eq_or_iff f ->
        let f1, f2 = destr_eq f in
        if is_tuple f1 && is_tuple f2 then
          let l = destr_tuple f1 in
          t_seq t_elim_eq_tuple (t_intros_elim (n+ List.length l - 1)) g
        else
          let id = LDecl.fresh_id hyps "H" in
          t_lseq [t_intros_i [id];t_try(t_subst1_id id);t_intros_elim (n-1)] g
      | _ ->
        let id = LDecl.fresh_id hyps "H" in
        t_seq (t_intros_i [id]) (t_intros_elim (n-1)) g
      end

    | _ -> tacuerror "nothing to introduce"

(* -------------------------------------------------------------------- *)
let t_congr (f1, f2) (args, ty) g =
  let rec doit args ty g =
    match args with
    | [] -> t_id None g

    | (a1, a2) :: args->
        let aty  = a1.f_ty in
        let m1   = f_app f1 (List.rev_map fst args) (tfun aty ty) in
        let m2   = f_app f2 (List.rev_map snd args) (tfun aty ty) in
        let tcgr = t_apply_logic EcCoreLib.p_fcongr
                     [ty; aty]
                     [AAform m2; AAform a1; AAform a2; AAnode] in

        let tsub g =
          let fx   = EcIdent.create "f" in
          let fty  = tfun aty ty in
          let body = f_app (f_local fx fty) [a1] ty in
          let lam  = EcFol.f_lambda [(fx, GTty fty)] body in
            t_subgoal
              [doit args fty]
              (t_apply_logic EcCoreLib.p_fcongr
                 [ty; fty]
                 [AAform lam; AAform m1; AAform m2; AAnode] g)
        in
          t_subgoal
            [tsub; tcgr]
            (t_transitivity (EcFol.f_app m2 [a1] ty) g)
  in
  doit (List.rev args) ty g

(* -------------------------------------------------------------------- *)
let t_logic_trivial =
  t_try
    (t_lseq [t_try t_assumption;
             t_progress (t_id None);
             t_assumption;
             t_fail])
back to top