/- Copyright (c) 2016 Gabriel Ebner. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Gabriel Ebner -/ import .clause .clause_ops import .prover_state .misc_preprocessing open expr list tactic monad decidable universe u namespace super meta def try_option {a : Type u} (tac : tactic a) : tactic (option a) := lift some tac <|> return none private meta def normalize : expr → tactic expr | e := do e' ← whnf e reducible, args' ← monad.for e'^.get_app_args normalize, return $ app_of_list e'^.get_app_fn args' meta def inf_normalize_l (c : clause) : tactic (list clause) := on_first_left c $ λtype, do type' ← normalize type, guard $ type' ≠ type, h ← mk_local_def `h type', return [([h], h)] meta def inf_normalize_r (c : clause) : tactic (list clause) := on_first_right c $ λha, do a' ← normalize ha^.local_type, guard $ a' ≠ ha^.local_type, hna ← mk_local_def `hna (imp a' c^.local_false), return [([hna], app hna ha)] meta def inf_false_l (c : clause) : tactic (list clause) := first $ do i ← list.range c^.num_lits, if c^.get_lit i = clause.literal.left ```(false) then [return []] else [] meta def inf_false_r (c : clause) : tactic (list clause) := on_first_right c $ λhf, if hf^.local_type = c^.local_false then return [([], hf)] else match hf^.local_type with | const ``false [] := do pr ← mk_app ``false.rec [c^.local_false, hf], return [([], pr)] | _ := failed end meta def inf_true_l (c : clause) : tactic (list clause) := on_first_left c $ λt, match t with | (const ``true []) := return [([], const ``true.intro [])] | _ := failed end meta def inf_true_r (c : clause) : tactic (list clause) := first $ do i ← list.range c^.num_lits, if c^.get_lit i = clause.literal.right (const ``true []) then [return []] else [] meta def inf_not_l (c : clause) : tactic (list clause) := on_first_left c $ λtype, match type with | app (const ``not []) a := do hna ← mk_local_def `h ```(%%a → false), return [([hna], hna)] | _ := failed end meta def inf_not_r (c : clause) : tactic (list clause) := on_first_right c $ λhna, match hna^.local_type with | app (const ``not []) a := do hnna ← mk_local_def `h ```((%%a → false) → %%c^.local_false), return [([hnna], app hnna hna)] | _ := failed end meta def inf_and_l (c : clause) : tactic (list clause) := on_first_left c $ λab, match ab with | (app (app (const ``and []) a) b) := do ha ← mk_local_def `l a, hb ← mk_local_def `r b, pab ← mk_mapp ``and.intro [some a, some b, some ha, some hb], return [([ha, hb], pab)] | _ := failed end meta def inf_and_r (c : clause) : tactic (list clause) := on_first_right' c $ λhyp, do pa ← mk_mapp ``and.left [none, none, some hyp], pb ← mk_mapp ``and.right [none, none, some hyp], return [([], pa), ([], pb)] meta def inf_iff_l (c : clause) : tactic (list clause) := on_first_left c $ λab, match ab with | (app (app (const ``iff []) a) b) := do hab ← mk_local_def `l (imp a b), hba ← mk_local_def `r (imp b a), pab ← mk_mapp ``iff.intro [some a, some b, some hab, some hba], return [([hab, hba], pab)] | _ := failed end meta def inf_iff_r (c : clause) : tactic (list clause) := on_first_right' c $ λhyp, do pa ← mk_mapp ``iff.mp [none, none, some hyp], pb ← mk_mapp ``iff.mpr [none, none, some hyp], return [([], pa), ([], pb)] meta def inf_or_r (c : clause) : tactic (list clause) := on_first_right c $ λhab, match hab^.local_type with | (app (app (const ``or []) a) b) := do hna ← mk_local_def `l (imp a c^.local_false), hnb ← mk_local_def `r (imp b c^.local_false), proof ← mk_app ``or.elim [a, b, c^.local_false, hab, hna, hnb], return [([hna, hnb], proof)] | _ := failed end meta def inf_or_l (c : clause) : tactic (list clause) := on_first_left c $ λab, match ab with | (app (app (const ``or []) a) b) := do ha ← mk_local_def `l a, hb ← mk_local_def `l b, pa ← mk_mapp ``or.inl [some a, some b, some ha], pb ← mk_mapp ``or.inr [some a, some b, some hb], return [([ha], pa), ([hb], pb)] | _ := failed end meta def inf_all_r (c : clause) : tactic (list clause) := on_first_right' c $ λhallb, match hallb^.local_type with | (pi n bi a b) := do ha ← mk_local_def `x a, return [([ha], app hallb ha)] | _ := failed end lemma imp_l {F a b} [decidable a] : ((a → b) → F) → ((a → F) → F) := λhabf haf, decidable.by_cases (assume ha : a, haf ha) (assume hna : ¬a, habf (take ha, absurd ha hna)) lemma imp_l' {F a b} [decidable F] : ((a → b) → F) → ((a → F) → F) := λhabf haf, decidable.by_cases (assume hf : F, hf) (assume hnf : ¬F, habf (take ha, absurd (haf ha) hnf)) lemma imp_l_c {F : Prop} {a b} : ((a → b) → F) → ((a → F) → F) := λhabf haf, classical.by_cases (assume hf : F, hf) (assume hnf : ¬F, habf (take ha, absurd (haf ha) hnf)) meta def inf_imp_l (c : clause) : tactic (list clause) := on_first_left_dn c $ λhnab, match hnab^.local_type with | (pi _ _ (pi _ _ a b) _) := if b^.has_var then failed else do hna ← mk_local_def `na (imp a c^.local_false), pf ← first (do r ← [``super.imp_l, ``super.imp_l', ``super.imp_l_c], [mk_app r [hnab, hna]]), hb ← mk_local_def `b b, return [([hna], pf), ([hb], app hnab (lam `a binder_info.default a hb))] | _ := failed end meta def inf_ex_l (c : clause) : tactic (list clause) := on_first_left c $ λexp, match exp with | (app (app (const ``Exists [u]) dom) pred) := do hx ← mk_local_def `x dom, predx ← whnf $ app pred hx, hpx ← mk_local_def `hpx predx, return [([hx,hpx], app_of_list (const ``exists.intro [u]) [dom, pred, hx, hpx])] | _ := failed end lemma demorgan' {F a} {b : a → Prop} : ((∀x, b x) → F) → (((∃x, b x → F) → F) → F) := assume hab hnenb, classical.by_cases (assume h : ∃x, ¬b x, begin cases h with x, apply hnenb, existsi x, intros, contradiction end) (assume h : ¬∃x, ¬b x, hab (take x, classical.by_cases (assume bx : b x, bx) (assume nbx : ¬b x, begin assert hf : false, apply h, existsi x, assumption, contradiction end))) meta def inf_all_l (c : clause) : tactic (list clause) := on_first_left_dn c $ λhnallb, match hnallb^.local_type with | pi _ _ (pi n bi a b) _ := do enb ← mk_mapp ``Exists [none, some $ lam n binder_info.default a (imp b c^.local_false)], hnenb ← mk_local_def `h (imp enb c^.local_false), pr ← mk_app ``super.demorgan' [hnallb, hnenb], return [([hnenb], pr)] | _ := failed end meta def inf_ex_r (c : clause) : tactic (list clause) := do (qf, ctx) ← c^.open_constn c^.num_quants, skolemized ← on_first_right' qf $ λhexp, match hexp^.local_type with | (app (app (const ``Exists [_]) d) p) := do sk_sym_name_pp ← get_unused_name `sk (some 1), inh_lc ← mk_local' `w binder_info.implicit d, sk_sym ← mk_local_def sk_sym_name_pp (pis (ctx ++ [inh_lc]) d), sk_p ← whnf_no_delta $ app p (app_of_list sk_sym (ctx ++ [inh_lc])), sk_ax ← mk_mapp ``Exists [some (local_type sk_sym), some (lambdas [sk_sym] (pis (ctx ++ [inh_lc]) (imp hexp^.local_type sk_p)))], sk_ax_name ← get_unused_name `sk_axiom (some 1), assert sk_ax_name sk_ax, nonempt_of_inh ← mk_mapp ``nonempty.intro [some d, some inh_lc], eps ← mk_mapp ``classical.epsilon [some d, some nonempt_of_inh, some p], existsi (lambdas (ctx ++ [inh_lc]) eps), eps_spec ← mk_mapp ``classical.epsilon_spec [some d, some p], exact (lambdas (ctx ++ [inh_lc]) eps_spec), sk_ax_local ← get_local sk_ax_name, cases sk_ax_local [sk_sym_name_pp, sk_ax_name], sk_ax' ← get_local sk_ax_name, return [([inh_lc], app_of_list sk_ax' (ctx ++ [inh_lc, hexp]))] | _ := failed end, return $ skolemized^.map (λs, s^.close_constn ctx) meta def first_some {a : Type} : list (tactic (option a)) → tactic (option a) | [] := return none | (x::xs) := do xres ← x, match xres with some y := return (some y) | none := first_some xs end private meta def get_clauses_core' (rules : list (clause → tactic (list clause))) : list clause → tactic (list clause) | cs := lift list.join $ do for cs $ λc, do first $ rules^.map (λr, r c >>= get_clauses_core') ++ [return [c]] meta def get_clauses_core (rules : list (clause → tactic (list clause))) (initial : list clause) : tactic (list clause) := do clauses ← get_clauses_core' rules initial, filter (λc, lift bnot $ is_taut c) $ list.nub_on clause.type clauses meta def clausification_rules_intuit : list (clause → tactic (list clause)) := [ inf_false_l, inf_false_r, inf_true_l, inf_true_r, inf_not_l, inf_not_r, inf_and_l, inf_and_r, inf_iff_l, inf_iff_r, inf_or_l, inf_or_r, inf_ex_l, inf_normalize_l, inf_normalize_r ] meta def clausification_rules_classical : list (clause → tactic (list clause)) := [ inf_false_l, inf_false_r, inf_true_l, inf_true_r, inf_not_l, inf_not_r, inf_and_l, inf_and_r, inf_iff_l, inf_iff_r, inf_or_l, inf_or_r, inf_imp_l, inf_all_r, inf_ex_l, inf_all_l, inf_ex_r, inf_normalize_l, inf_normalize_r ] meta def get_clauses_classical : list clause → tactic (list clause) := get_clauses_core clausification_rules_classical meta def get_clauses_intuit : list clause → tactic (list clause) := get_clauses_core clausification_rules_intuit meta def as_refutation : tactic unit := do repeat (do intro1, skip), tgt ← target, if tgt^.is_constant || tgt^.is_local_constant then skip else do local_false_name ← get_unused_name `F none, tgt_type ← infer_type tgt, definev local_false_name tgt_type tgt, local_false ← get_local local_false_name, target_name ← get_unused_name `target none, assertv target_name (imp tgt local_false) (lam `hf binder_info.default tgt $ mk_var 0), change local_false meta def clauses_of_context : tactic (list clause) := do local_false ← target, l ← local_context, monad.for l (clause.of_proof local_false) meta def clausify_pre := preprocessing_rule $ take new, lift list.join $ for new $ λ dc, do cs ← get_clauses_classical [dc^.c], if cs^.length ≤ 1 then return (for cs $ λ c, { dc with c := c }) else for cs (λc, mk_derived c dc^.sc) -- @[super.inf] meta def clausification_inf : inf_decl := inf_decl.mk 0 $ λgiven, list.foldr orelse (return ()) $ do r ← clausification_rules_classical, [do cs ← r given^.c, cs' ← get_clauses_classical cs, for' cs' (λc, mk_derived c given^.sc^.sched_now >>= add_inferred), remove_redundant given^.id []] end super