12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019(* This file is free software, part of Zipperposition. See file "license" for more details. *)(** {1 boolean subterms} *)openLogtkopenLibzipperpositionmoduleT=TermmodulePos=PositionmoduleUS=Unif_substtypeselection_setting=Any|Minimal|Largetypereasoning_kind=BoolReasoningDisabled|BoolCasesInference|BoolCasesDisabled|BoolCasesSimplification|BoolCasesKeepParent|BoolCasesPreprocessletsection=Util.Section.make~parent:Const.section"booleans"letk_bool_reasoning=Flex_state.create_key()letk_cased_term_selection=Flex_state.create_key()letk_quant_rename=Flex_state.create_key()letk_interpret_bool_funs=Flex_state.create_key()letk_cnf_non_simpl=Flex_state.create_key()letk_norm_bools=Flex_state.create_key()letk_filter_literals=Flex_state.create_key()letk_nnf=Flex_state.create_key()letk_elim_bvars=Flex_state.create_key()letk_simplify_bools=Flex_state.create_key()letselection_to_str=function|Any->"any"|Minimal->"minimal"|Large->"large"moduletypeS=sigmoduleEnv:Env.SmoduleC:moduletypeofEnv.C(** {5 Registration} *)valsetup:unit->unit(** Register rules in the environment *)endmoduleMake(E:Env.S):SwithmoduleEnv=E=structmoduleEnv=EmoduleC=Env.CmoduleCtx=Env.CtxmoduleFool=Fool.Make(Env)moduleCombs=Combinators.Make(Env)moduleHO=Higher_order.Make(Env)moduleLazyCNF=Lazy_cnf.Make(Env)let(=~),(/~)=Literal.mk_eq,Literal.mk_neqlet(@:)=T.app_builtin~ty:Type.propletnoa=a=~T.false_letyesa=a=~T.true_letfind_boolsc=letfound=reffalseinletsubterm_selection=Env.flex_getk_cased_term_selectioninletrecfind_in_term~toptk=matchT.viewtwith|T.Const_whenType.is_prop(T.tyt)&¬top->kt|T.App(_,args)|T.AppBuiltin(_,args)->lettake_subterm=nottop&&Type.is_prop(T.tyt)&¬(T.is_true_or_falset)&&T.DB.is_closedt&¬(T.is_var(T.head_termt))&¬(T.is_bvar(T.head_termt))&&(subterm_selection!=Minimal||Iter.is_empty(Iter.flat_map(find_in_term~top:false)(CCList.to_iterargs)))inletcontinue=(subterm_selection=Any||nottake_subterm)&&(* do not traverse variable-headed terms *)not(T.is_var(T.head_termt))&¬(T.is_bvar(T.head_termt))iniftake_subtermthenkt;ifcontinuethen(List.iter(funarg->find_in_term~top:(top&&T.is_appbuiltint)argk)args)|T.Fun(_,body)->find_in_term~topbodyk|_->()inleteligible=matchEnv.flex_getk_filter_literalswith|`All->C.Eligible.always|`Max->(matchsubterm_selectionwith|Any->C.Eligible.resc|_->(funilit->(* found gives us the leftmost match! *)not(!found)&&C.Eligible.rescilit))inLiterals.fold_terms~which:`All~subterms:false~eligible~ord:(C.Ctx.ord())(C.litsc)|>Iter.flat_map(fun(t,_)->letres=find_in_term~top:truetinifnot(Iter.is_emptyres)thenfound:=true;res)|>T.Set.of_iter|>T.Set.to_listletmk_res~proof~old~replnew_litc=C.create~trail:(C.trailc)~penalty:(C.penaltyc)(new_lit::Array.to_list(C.litsc|>Literals.map(T.replace~old~by:repl)))proofletbool_case_inf(c:C.t):C.tlist=letproof=Proof.Step.inference[C.proof_parentc]~rule:(Proof.Rule.mk"bool_inf")~tags:[Proof.Tag.T_ho]inUtil.debugf5~section"bci(@[%a@])=@."(funk->kC.ppc);find_boolsc|>CCList.fold_left(funaccold->letneg_lit,repl=ifBuiltin.compareBuiltin.TrueBuiltin.False>0then(noold,T.true_)else(yesold,T.false_)in(mk_res~proof~old~replneg_litc)::acc)[]letbool_case_simp(c:C.t):C.tlistoption=letproof=Proof.Step.simp[C.proof_parentc]~rule:(Proof.Rule.mk"bool_simp")~tags:[Proof.Tag.T_ho]inletbool_subterms=find_boolscinifCCList.is_emptybool_subtermsthenNoneelse(CCOpt.return@@CCList.fold_left(funaccold->letneg_lit,repl_neg=noold,T.true_inletpos_lit,repl_pos=yesold,T.false_in(mk_res~proof~old~repl:repl_negneg_litc)::(mk_res~proof~old~repl:repl_pospos_litc)::acc)[]bool_subterms)letsimplify_boolst=letnegatet=matchT.viewtwith|T.AppBuiltin(((Builtin.Eq|Builtin.Neq)asb),l)->lethd=ifb=Builtin.EqthenBuiltin.NeqelseBuiltin.EqinT.app_builtin~ty:(T.tyt)hdl|T.AppBuiltin(Builtin.Not,[s])->s|_->T.Form.not_tinletsimplify_and_ortbl=letopenTerminletcompl_in_ll=letpos,neg=CCList.partition_map(funt->matchviewtwith|AppBuiltin(Builtin.Not,[s])->`Rights|_->`Leftt)l|>CCPair.map_sameSet.of_listinnot(Set.is_empty(Set.interposneg))inletres=assert(b=Builtin.And||b=Builtin.Or);letnetural_el,absorbing_el=ifb=Builtin.Andthentrue_,false_else(false_,true_)inletl'=CCList.sort_uniq~cmp:comparelinifcompl_in_ll||List.exists(equalabsorbing_el)lthenabsorbing_elelse(letl'=List.filter(funs->not(equalsnetural_el))l'inifList.lengthl=List.lengthl'thentelse(ifCCList.is_emptyl'thennetural_elelse(ifList.lengthl'=1thenList.hdl'elseapp_builtin~ty:(Type.prop)bl')))inresinletrecauxt=letty_is_propt=Type.is_prop(T.tyt)inmatchT.viewtwith|DB_|Const_|Var_->t|Fun(ty,body)->letbody'=auxbodyinassert(Type.equal(T.tybody)(T.tybody'));ifT.equalbodybody'thentelseT.fun_tybody'|App(hd,args)->lethd'=auxhdandargs'=List.mapauxargsinifT.equalhdhd'&&T.same_largsargs'thentelseT.apphd'args'|AppBuiltin(Builtin.And,[x])whenT.is_true_or_falsex&&ty_is_propt&&List.length(Type.expected_args(T.tyt))=1->ifT.equalxT.true_then(T.fun_Type.prop(T.bvar~ty:Type.prop0))else(assert(T.equalxT.false_);T.fun_Type.propT.false_)|AppBuiltin(Builtin.Or,[x])whenT.is_true_or_falsex&&ty_is_propt&&List.length(Type.expected_args(T.tyt))=1->letprop=Type.propinifT.equalxT.true_then(T.fun_prop(T.true_))else(assert(T.equalxT.false_);T.fun_prop(T.bvar~ty:prop0))|AppBuiltin(Builtin.And,l)whenty_is_propt&&List.lengthl>1->letl'=List.mapauxlinlett=ifT.same_lll'thentelseT.app_builtin~ty:(Type.prop)Builtin.Andl'insimplify_and_ortBuiltin.Andl'|AppBuiltin(Builtin.Or,l)whenty_is_propt&&List.lengthl>1->letl'=List.mapauxlinlett=ifT.same_lll'thentelseT.app_builtin~ty:(Type.prop)Builtin.Orl'insimplify_and_ortBuiltin.Orl'|AppBuiltin(Builtin.Not,[s])->lets'=auxsinbeginmatchT.views'with|AppBuiltin(Builtin.Not,[s''])->s''|_->ifT.equals'T.true_thenT.false_elseifT.equals'T.false_thenT.true_elseifT.equalss'thentelseT.app_builtin~ty:(Type.prop)Builtin.Not[s']end|AppBuiltin(Builtin.Imply,[p;c])->letunroll_andp=matchT.viewpwith|AppBuiltin(And,l)->T.Set.of_listl|_->T.Set.singletonpinletunroll_orp=matchT.viewpwith|AppBuiltin(Or,l)->T.Set.of_listl|_->T.Set.singletonpinletis_implp=matchT.viewpwith|AppBuiltin(Imply,[l;r])->true|_->falsein(* Take a term of the form p11 /\ ... /\ p1n1 -> p21 /\ ... /\ p2n2 -> ... -> q1 \/ ... \/ qn
and return the set of premises {p11,...} and conclusions {q1, ... }
*)letunroll_implp=assert(is_implp);letrecauxaccp=matchT.viewpwith|AppBuiltin(Imply,[l;r])->letunrolled_l=unroll_andlinletacc'=Term.Set.unionunrolled_laccinifis_implrthenauxacc'relse(acc',unroll_orr)|_->assertfalseinauxTerm.Set.emptypinletp'=auxpandc'=auxcinlet(premises,conclusions)=unroll_impl(T.Form.implyp'c')inifnot(T.Set.is_empty(T.Set.interpremisesconclusions))then(T.true_)elseifT.equalp'c'thenT.true_elseifT.equalc'(negatep')thenc'elseifT.equalp'(negatec')thenc'elseifT.equalp'T.true_thenc'elseifT.equalp'T.false_thenT.true_elseifT.equalc'T.false_thenaux(T.Form.not_p')elseifT.equalc'T.true_thenT.true_else(ifT.equalpp'&&T.equalcc'thentelseT.app_builtin~ty:(T.tyt)Builtin.Imply[p';c'])|AppBuiltin((Builtin.Eq|Builtin.Equiv)ashd,([a;b]|[_;a;b]))->leta',b'=auxa,auxbinifT.equala'b'thenT.true_elseifT.equala'T.true_thenb'elseifT.equalb'T.true_thena'elseifT.equala'T.false_thenaux(T.Form.not_b')elseifT.equalb'T.false_thenaux(T.Form.not_a')else(ifT.equalaa'&&T.equalbb'thentelseT.app_builtin~ty:(T.tyt)hd[a';b'])|AppBuiltin((Builtin.Neq|Builtin.Xor)ashd,([a;b]|[_;a;b]))->leta',b'=auxa,auxbinifT.equala'b'thenT.false_elseifT.equala'T.true_thenaux(T.Form.not_b')elseifT.equalb'T.true_thenaux(T.Form.not_a')elseifT.equala'T.false_thenb'elseifT.equalb'T.false_thena'else(ifT.equalaa'&&T.equalbb'thentelseT.app_builtin~ty:(T.tyt)hd[a';b'])|AppBuiltin((ExistsConst|ForallConst)asb,[g])->letg'=auxginletexp_g=Combs.expandg'inlet_,body=T.open_funexp_ginassert(Type.is_prop(T.tybody));if(T.Seq.subterms~include_builtin:truebody|>Iter.existsT.is_bvar)then(ifT.equalgg'thentelseT.app_builtin~ty:(T.tyt)b[g'])elsebody|AppBuiltin(hd,args)->letargs'=List.mapauxargsinifT.same_largsargs'thentelseT.app_builtin~ty:(T.tyt)hdargs'inletres=auxtinassert(T.DB.is_closedres);resletsimpl_bool_subtermsc=tryletnew_lits=Literals.mapsimplify_bools(C.litsc)inifLiterals.equal(C.litsc)new_litsthen(SimplM.return_samec)else(letproof=Proof.Step.simp[C.proof_parentc]~rule:(Proof.Rule.mk"simplify boolean subterms")inletnew_=C.create~trail:(C.trailc)~penalty:(C.penaltyc)(Array.to_listnew_lits)proofinSimplM.return_newnew_)withType.ApplyErrorerr->CCFormat.printf"error(%s):@[%a@]@."errC.ppc;CCFormat.printf"@[%a@]@."Proof.S.pp_tstp(C.proofc);assertfalseletnnf_boolst=letmoduleF=T.Forminletrecauxt=matchT.viewtwith|Const_|DB_|Var_->t|Fun_->lettyargs,body=T.open_funtinletbody'=auxbodyinifT.equalbodybody'thentelseT.fun_ltyargsbody'|App(hd,l)->lethd'=auxhdandl'=List.mapauxlinifT.equalhdhd'&&T.same_lll'thentelseT.apphd'l'|AppBuiltin(Builtin.Not,[f])->beginmatchT.viewfwith|AppBuiltin(Not,[g])->auxg|AppBuiltin(((And|Or)asb),l)whenList.lengthl>=2->letflipped=ifb=Builtin.AndthenF.or_lelseF.and_linflipped(List.map(funt->aux(F.not_t))l)|AppBuiltin(((ForallConst|ExistsConst)asb),([g]|[_;g]))->letflipped=ifb=Builtin.ForallConstthenBuiltin.ExistsConstelseBuiltin.ForallConstinletg_ty_args,g_body=T.open_fun(Combs.expandg)inletg_body'=aux@@F.not_g_bodyinletg'=Lambda.eta_reduce(T.fun_lg_ty_argsg_body')inT.app_builtin~ty:(T.tyt)flipped[g']|AppBuiltin(Imply,[g;h])->F.and_(auxg)(aux@@F.not_h)|AppBuiltin(((Equiv|Xor)asb),[g;h])->letflipped=ifb=EquivthenBuiltin.XorelseBuiltin.Equivinaux(T.app_builtin~ty:(T.tyt)flipped[g;h])|AppBuiltin(((Eq|Neq)asb),([_;s;t]|[s;t]))->letflipped=ifb=EqthenF.neqelseF.eqinflipped(auxs)(auxt)|_->F.not_(auxf)end|AppBuiltin(Imply,[f;g])->aux(F.or_(F.not_f)g)|AppBuiltin(Equiv,[f;g])->aux(F.and_(F.implyfg)(F.implygf))|AppBuiltin(Xor,[f;g])->aux(F.and_(F.or_fg)(F.or_(F.not_f)(F.not_g)))|AppBuiltin(b,l)->letl'=List.mapauxlinifT.same_lll'thentelseT.app_builtin~ty:(T.tyt)bl'inauxtletnnf_bool_subtersc=letnew_lits=Literals.mapnnf_bools(C.litsc)inifLiterals.equal(C.litsc)new_litsthen(SimplM.return_samec)else(letproof=Proof.Step.simp[C.proof_parentc]~rule:(Proof.Rule.mk"nnf boolean subterms")inletnew_=C.create~trail:(C.trailc)~penalty:(C.penaltyc)(Array.to_listnew_lits)proofinSimplM.return_newnew_)letnormalize_bool_termsc=letnew_lits=Literals.mapT.normalize_bools(C.litsc)inifLiterals.equal(C.litsc)new_litsthen(SimplM.return_samec)else(letproof=Proof.Step.simp[C.proof_parentc]~rule:(Proof.Rule.mk"normalize subterms")inletnew_=C.create~trail:(C.trailc)~penalty:(C.penaltyc)(Array.to_listnew_lits)proofinSimplM.return_newnew_)letcnf_otfc:C.tlistoption=letidx=CCArray.find_idx(funl->leteq=Literal.View.as_eqnlinmatcheqwith|Some(l,r,sign)->Type.is_prop(T.tyl)&¬(T.equallr)&&((not(T.equalrT.true_)&¬(T.equalrT.false_))||T.is_formulal||T.is_formular)|None->false)(C.litsc)inletrenaming_weight=40inletmax_formula_weight=C.Seq.termsc|>Iter.filterT.is_formula|>Iter.mapT.size|>Iter.maxinletopts=matchmax_formula_weightwith|None->[Cnf.DisableRenaming]|Somem->ifm<renaming_weightthen[Cnf.DisableRenaming]else[]inmatchidxwith|Some_->letf=Literals.Conv.to_tst(C.litsc)inletproof=Proof.Step.simp~rule:(Proof.Rule.mk"cnf_otf")~tags:[Proof.Tag.T_ho][C.proof_parentc]inlettrail=C.trailcandpenalty=C.penaltycinletstmt=Statement.assert_~prooffinletcnf_vec=Cnf.convert@@CCVector.to_iter@@Cnf.cnf_of~opts~ctx:(Ctx.sk_ctx())stmtinCCVector.iter(funcl->Statement.Seq.ty_declscl|>Iter.iter(fun(id,ty)->Ctx.declareidty))cnf_vec;letsolved=ifEnv.flex_getLazy_cnf.k_solve_formulasthen(CCOpt.get_or~default:[](LazyCNF.solve_bool_formulasc))else[]inletclauses=CCVector.map(C.of_statement~convert_defs:true)cnf_vec|>CCVector.to_list|>CCList.flatten|>List.map(func->C.create~penalty~trail(CCArray.to_list(C.litsc))proof)inUtil.debugf~section1"cl:@[%a@]@."(funk->kC.ppc);Util.debugf~section1" @[%a@]@."(funk->k(CCList.ppC.pp)clauses);List.iteri(funinew_c->assert((C.proof_depthc)<=C.proof_depthnew_c);)clauses;Some(solved@clauses)|None->Noneletcnf_infercl=CCOpt.get_or~default:[](cnf_otfcl)letelim_bvarsc=C.Seq.termsc|>Iter.filter_map(funv->ifT.is_varv&&Type.is_prop(T.tyv)then(Some(T.as_var_exnv))elseNone)|>Iter.sort_uniq~cmp:(HVar.compareType.compare)|>Iter.flat_map_l(funv->letsubst_true=Subst.FO.bind'Subst.empty(v,0)(T.true_,0)inletsubst_false=Subst.FO.bind'Subst.empty(v,0)(T.false_,0)in[subst_true;subst_false])|>Iter.map(funsubst->letproof=Some(Proof.Step.simp~tags:[Proof.Tag.T_ho]~rule:(Proof.Rule.mk"elim_bool_vars")[C.proof_parentc])inC.apply_subst~proof~penalty_inc:(Some(-1))(c,0)subst)|>Iter.to_listletinterpret_boolean_functionsc=(* Collects boolean functions only at top level,
and not the ones that are already a part of the quantifier *)letcollect_tl_bool_funstk=letrecauxt=letty_args,ret_ty=Type.open_fun(T.tyt)inifnot(CCList.is_emptyty_args)&&Type.is_propret_ty&¬(T.is_vart)&¬(T.is_app_vart)thenktelse(matchT.viewtwith|App(f,l)->(* head positions are not taken into account *)List.iterauxl|AppBuiltin(b,l)whennot(Builtin.is_quantifierb)->List.iterauxl|_->())inauxtinletinterpretti=letty_args,body=T.open_funtinassert(Type.is_prop(Term.tybody));T.fun_lty_argsiinletnegate_bool_funbool_fun=letty_args,body=T.open_funbool_funinassert(Type.is_prop(Term.tybody));T.fun_lty_args(T.Form.not_body)inletforall_closet=letty_args,body=T.open_funtinassert(Type.is_prop(T.tybody));List.fold_right(funtyacc->T.Form.forall(T.fun_tyacc))ty_argsbodyinIter.flat_mapcollect_tl_bool_funs(C.Seq.termsc(* If the term is a top-level function, then apply extensionality,
not this rule on it. *)|>Iter.filter(funt->not@@Type.is_fun(T.tyt)))(* avoiding terms introduced by primitive enumeration *)|>Iter.filter(funt->letcached_t=Subst.FO.canonize_all_varstinnot(Term.Set.memcached_t!Higher_order.prim_enum_terms))|>Iter.sort_uniq~cmp:Term.compare|>Iter.fold(funrest->assert(T.DB.is_closedt);letproof=Proof.Step.inference[C.proof_parentc]~rule:(Proof.Rule.mk"interpret boolean function")~tags:[Proof.Tag.T_ho]inlett'=Combs.expandtinlet_,t'_body=T.open_funt'inifnot(T.is_true_or_falset'_body)then(letas_forall=Literal.mk_prop(forall_closet')falseinletas_neg_forall=Literal.mk_prop(forall_close(negate_bool_funt'))falseinletforall_cl=C.create~trail:(C.trailc)~penalty:(C.penaltyc)(as_forall::Array.to_list(C.litsc|>Literals.map(T.replace~old:t~by:(interprett'T.true_))))proofinletforall_neg_cl=C.create~trail:(C.trailc)~penalty:(C.penaltyc)(as_neg_forall::Array.to_list(C.litsc|>Literals.map(T.replace~old:t~by:(interprett'T.false_))))proofinUtil.debugf~section1"interpret bool(@[%a@]):@.@[%a@] !!> @. @[%a@]@."(funk->kT.pptLiterals.pp(C.litsc)Literals.pp(C.litsforall_cl));Util.debugf~section1"interpret bool(@[%a@]):@.@[%a@] !!> @. @[%a@]@."(funk->kT.pptLiterals.pp(C.litsc)Literals.pp(C.litsforall_neg_cl));forall_cl::forall_neg_cl::res)elseres)[]letnormalize_equalitiesc=letlits=Array.to_list(C.litsc)inletnormalized=List.mapLiteral.normalize_eqlitsinifList.existsCCOpt.is_somenormalizedthen(letnew_lits=List.mapi(funil_opt->CCOpt.get_or~default:(Array.get(C.litsc)i)l_opt)normalizedinletproof=Proof.Step.simp[C.proof_parentc]~rule:(Proof.Rule.mk"simplify nested equalities")inletnew_c=C.create~trail:(C.trailc)~penalty:(C.penaltyc)new_litsproofinSimplM.return_newnew_c)else(SimplM.return_samec)letsetup()=Env.add_basic_simplifynormalize_equalities;matchEnv.flex_getk_bool_reasoningwith|BoolReasoningDisabled->()|_->ifEnv.flex_getk_simplify_boolsthen(Env.add_basic_simplifysimpl_bool_subterms);ifEnv.flex_getk_nnfthen(E.add_basic_simplifynnf_bool_subters;);ifEnv.flex_getk_norm_boolsthen(Env.add_basic_simplifynormalize_bool_terms);ifEnv.flex_getk_elim_bvarsthen(Env.add_unary_inf"elim_bvars"elim_bvars;);ifnot!Lazy_cnf.enabledthen(Env.add_multi_simpl_rule~priority:2Fool.rw_bool_lits;ifEnv.flex_getk_cnf_non_simplthen(Env.add_unary_inf"cnf otf inf"cnf_infer;)elseEnv.add_multi_simpl_rule~priority:2cnf_otf);if(Env.flex_getk_interpret_bool_funs)then(Env.add_unary_inf"interpret boolean functions"interpret_boolean_functions;);ifEnv.flex_getk_bool_reasoning=BoolCasesInferencethen(Env.add_unary_inf"bool_cases"bool_case_inf;)elseifEnv.flex_getk_bool_reasoning=BoolCasesSimplificationthen(Env.add_single_step_multi_simpl_rulebool_case_simp;)elseifEnv.flex_getk_bool_reasoning=BoolCasesKeepParentthen(letkeep_parentc=CCOpt.get_or~default:[](bool_case_simpc)inEnv.add_unary_inf"bool_cases_keep_parent"keep_parent;)endopenCCFunopenBuiltinopenStatementopenTypedSTermopenCCListletif_changedproof(mk:?attrs:Logtk.Statement.attrs->'r)sfp=letfp=fspiniffp==[p]then[s]elsemap(funx->mk~proof:(proofs)x)fp(* match fp with
| [ x ] when TypedSTerm.equal x p -> [s]
| _ -> map(fun x -> mk ~proof:(proof s) x) fp *)letmap_propositions~prooff=CCVector.flat_map_list(funs->matchStatement.viewswith|Assertp->if_changedproofassert_sfp|Lemmaps->if_changedprooflemmas(map%f)ps|Goalp->if_changedproofgoalsfp|NegatedGoal(ts,ps)->if_changedproof(neg_goal~skolems:ts)s(map%f)ps|_->[s])letis_boolt=CCOpt.equalTy.equal(Someprop)(tyt)letis_T_Ft=matchviewtwithAppBuiltin((True|False),[])->true|_->false(* Modify every subterm of t by f except those at the "top". Here top is true if subterm occurs under a quantifier Æ in a context where it could participate to the clausification if the surrounding context of Æ was ignored. *)letrecreplaceTSTftopt=letre=replaceTSTfinletty=ty_exntinlettransformer=iftopthenidelsefintransformer(matchviewtwith|App(t,ts)->app_whnf~ty(refalset)(map(refalse)ts)|Ite(c,x,y)->ite(refalsec)(refalsex)(refalsey)|Match(t,cases)->match_(refalset)(map(fun(c,vs,e)->(c,vs,refalsee))cases)|Let(binds,expr)->let_(map(CCPair.map_snd(refalse))binds)(refalseexpr)|Bind(b,x,t)->lettop=Binder.equalbBinder.Forall||Binder.equalbBinder.Existsinbind~tybx(retopt)|AppBuiltin(b,ts)->letlogical=for_allis_booltsinapp_builtin~tyb(map(re(top&&logical))ts)|Multisetts->multiset~ty(map(refalse)ts)|_->t)letname_quantifiersstmts=letproofs=Proof.Step.esa[Proof.Parent.from(Statement.as_proof_is)]~rule:(Proof.Rule.mk"Quantifier naming")inletnew_stmts=CCVector.create()inletchanged=reffalseinletif_changed(mk:?attrs:Logtk.Statement.attrs->'r)sr=if!changedthen(changed:=false;mk~proof:(proofs)r)elsesinletif_changed_list(mk:?attrs:Logtk.Statement.attrs->'l)sr=if!changedthen(changed:=false;mk~proof:(proofs)r)elsesinletname_prop_Qss=replaceTST(funt->matchTypedSTerm.viewtwith|Bind(Binder.Forall,_,_)|Bind(Binder.Exists,_,_)->changed:=true;letvars=Var.Set.of_iter(TypedSTerm.Seq.free_varst)|>Var.Set.to_listinletqid=ID.gensym()inletty=app_builtin~ty:tTypeArrow(prop::mapVar.tyvars)inletq=const~tyqidinletq_vars=app~ty:propq(mapvarvars)inletproof=Proof.Step.define_internalqid[Proof.Parent.from(Statement.as_proof_is)]inletq_typedecl=ty_decl~proofqidtyinletdefinition=(* ∀ vars: q[vars] ⇔ t, where t is a quantifier formula and q is a new name for it. *)bind_list~ty:propBinder.Forallvars(app_builtin~ty:propBuiltin.Equiv[q_vars;t])inCCVector.pushnew_stmtsq_typedecl;CCVector.pushnew_stmts(assert_~proofdefinition);q_vars|_->t)trueinstmts|>CCVector.map(funs->matchStatement.viewswith|TyDecl(id,t)->s|Datats->s|Defdefs->s|Rewrite_->s|Assertp->if_changedassert_s(name_prop_Qssp)|Lemmaps->if_changed_listlemmas(map(name_prop_Qss)ps)|Goalp->if_changedgoals(name_prop_Qssp)|NegatedGoal(ts,ps)->if_changed_list(neg_goal~skolems:ts)s(map(name_prop_Qss)ps))|>CCVector.appendnew_stmts;CCVector.freezenew_stmtsletrecreplaceoldbyt=letr=replaceoldbyinletty=ty_exntinifTypedSTerm.equaltoldthenbyelsematchviewtwith|App(f,ps)->app_whnf~ty(rf)(maprps)|AppBuiltin(f,ps)->app_builtin~tyf(maprps)|Ite(c,x,y)->ite(rc)(rx)(ry)|Let(bs,e)->let_(map(CCPair.map_sndr)bs)(re)|Bind(b,v,e)->bind~tybv(re)|_->texceptionReturnofTypedSTerm.t(* If f _ s = Some r for a subterm s of t, then r else t. *)letwith_subterm_or_idtf=try(Seq.subterms_with_boundt(fun(s,var_ctx)->matchfvar_ctxswith|None->()|Somer->raise(Returnr)));twithReturnr->r(* If p is non-constant subproposition closed wrt variables vs, then (p ⇒ c[p:=⊤]) ∧ (p ∨ c[p:=⊥]) or else c unmodified. *)letcase_boolvscp=ifis_boolp&¬(is_T_Fp)&¬(TypedSTerm.equalpc)&&Var.Set.is_empty(Var.Set.diff(free_vars_setp)vs)thenletty=propinapp_builtin~tyAnd[app_builtin~tyImply[p;replacepForm.true_c];app_builtin~tyOr[p;replacepForm.false_c];]elsec(* Apply repeatedly the transformation t[p] ↦ (p ⇒ t[⊤]) ∧ (¬p ⇒ t[⊥]) for each boolean parameter p≠⊤,⊥ that is closed in context where variables vs are bound. *)letreccase_bools_wrtvst=with_subterm_or_idt(fun_s->matchviewswith|App(f,ps)->lett'=fold_left(case_boolvs)tpsinifTypedSTerm.equaltt'thenNoneelseSome(case_bools_wrtvst')|_->None)leteager_cases_farstms=letproofs=Proof.Step.esa[Proof.Parent.from(Statement.as_proof_is)]~rule:(Proof.Rule.mk"eager_cases_far")inmap_propositions~proof(fun_t->[with_subterm_or_idt(funvss->matchviewswith|Bind((Forall|Exists)asq,v,b)->letb'=case_bools_wrt(Var.Set.addvsv)binifTypedSTerm.equalbb'thenNoneelseSome(replaces(bind~ty:propqvb')t)|_->None)|>case_bools_wrtVar.Set.empty])stmsleteager_cases_nearstms=letproofs=Proof.Step.esa[Proof.Parent.from(Statement.as_proof_is)]~rule:(Proof.Rule.mk"eager_cases_near")inletmoduleT=TypedSTerminletfind_fool_subtermp=letrecaux~topp=letp_ty=T.ty_exnpinletreturnp=assert(T.Ty.is_prop(T.ty_exnp));assert(T.closedp);Some(T.Form.true_,T.Form.false_,p)inmatchT.viewpwith|AppBuiltin(hd,args)whennottop&&T.closedp&&T.Ty.is_prop(T.ty_exnp)&&(* making sure it is not T or F *)(Builtin.is_logical_ophd||Builtin.equalhdBuiltin.Eq||Builtin.equalhdBuiltin.Neq)->returnp|Bind((Binder.Exists|Binder.Forall),var,body)whennottop&&T.closedp->returnp|Bind(Binder.Lambda,var,body)->CCOpt.map(fun(body_t,body_f,s)->assert(T.closeds);(T.fun_l[var]body_t,T.fun_l[var]body_f,s))(aux~top:falsebody)|App(hd,args)whennottop&&T.Ty.is_propp_ty&&T.closedp->returnp|Const_whennottop&&T.Ty.is_propp_ty->returnp|AppBuiltin(b,args)->CCOpt.map(fun(args_t,args_f,s)->(T.app_builtin~ty:p_tybargs_t,T.app_builtin~ty:p_tybargs_f,s))(aux_largs)|App(hd,args)->CCOpt.map(fun(args_t,args_f,s)->(T.app~ty:p_tyhdargs_t,T.app~ty:p_tyhdargs_f,s))(aux_largs)|_->Noneandaux_l=function|[]->None|x::xs->beginmatchaux~top:falsexwith|Some(x_t,x_f,s)->Some(x_t::xs,x_f::xs,s)|None->beginmatchaux_lxswith|Some(xs_t,xs_f,s)->Some(x::xs_t,x::xs_f,s)|None->Noneendendinletres=aux~top:truepinresinletunroll_foolp=letrecauxp=letp_ty=T.ty_exnpinmatchT.viewpwith|AppBuiltin(((Builtin.Neq|Builtin.Eq)ashd),[a;b])whennot(T.Ty.is_prop(T.ty_exna))->letcons=ifhd=NeqthenT.Form.neqelseT.Form.eqinbeginmatchfind_fool_subtermawith|None->beginmatchfind_fool_subtermbwith|None->p|Some(b_t,b_f,subterm)->letsubterm'=auxsubterminletif_true=T.Form.or_[T.Form.not_(subterm');aux@@consab_t]inletif_false=T.Form.or_[subterm';aux@@consab_f]inT.Form.and_[if_true;if_false]end|Some(a_t,a_f,subterm)->letsubterm'=auxsubterminletif_true=T.Form.or_[T.Form.not_(subterm');aux@@consa_tb]inletif_false=T.Form.or_[subterm';aux@@consa_fb]inT.Form.and_[if_true;if_false]end|AppBuiltin(hd,args)->T.app_builtin~ty:p_tyhd(List.mapauxargs)|App(hd,args)->beginmatchfind_fool_subtermpwith|Some(p_t,p_f,subterm)->letsubterm'=auxsubterminletif_true=T.Form.or_[T.Form.not_(subterm');auxp_t]inletif_false=T.Form.or_[subterm';auxp_f]inT.Form.and_[if_true;if_false]|None->pend|Bind((Binder.Exists|Binder.Forall)asb,var,body)->letbody'=auxbodyinT.bind~ty:p_tybvarbody'|_->pinletres=auxpinresinmap_propositions~proof(fun_p->[unroll_foolp])stmsopenTermletpost_eager_cases=letproofs=Proof.Step.esa[Proof.Parent.from(Statement.as_proof_cs)]~rule:(Proof.Rule.mk"post_eager_cases")inmap_propositions~proof(fun_c->letcased=refSet.emptyinfold_left(SLiteral.fold(funres->(* Loop over subterms of terms of literals of a clause. *)Seq.subterms_depth%>Iter.fold(funres(s,d)->ifd=0||not(Type.is_prop(tys))||is_true_or_falses||is_vars||Set.mems!cased||not(T.DB.is_closeds)thenreselse(cased:=Set.adds!cased;letreplace_s_byby=map(SLiteral.map~f:(replace~old:s~by))inflatten(map(func->[SLiteral.atom_trues::replace_s_byfalse_c;SLiteral.atom_falses::replace_s_bytrue_c])res)))res))[c]c)let_bool_reasoning=refBoolReasoningDisabledlet_quant_rename=reffalse(* These two options run before CNF,
so (for now it is impossible to move them to Env
since it is not even made at the moment) *)letpreprocess_booleansstmts=(match!_bool_reasoningwith|BoolCasesPreprocess->eager_cases_near|_->id)(if!_quant_renamethenname_quantifiersstmtselsestmts)letpreprocess_cnf_booleansstmts=match!_bool_reasoningwith|BoolCasesPreprocess->letres=post_eager_casesstmtsinres|_->stmtslet_cased_term_selection=refLargelet_interpret_bool_funs=reffalselet_cnf_non_simpl=reffalselet_norm_bools=reffalselet_filter_literals=ref`Maxlet_nnf=reffalselet_simplify_bools=reftruelet_elim_bvars=reftrueletextension=letregisterenv=letmoduleE=(valenv:Env.S)inletmoduleET=Make(E)inE.flex_addk_bool_reasoning!_bool_reasoning;E.flex_addk_cased_term_selection!_cased_term_selection;E.flex_addk_quant_rename!_quant_rename;E.flex_addk_interpret_bool_funs!_interpret_bool_funs;E.flex_addk_cnf_non_simpl!_cnf_non_simpl;E.flex_addk_norm_bools!_norm_bools;E.flex_addk_filter_literals!_filter_literals;E.flex_addk_nnf!_nnf;E.flex_addk_elim_bvars!_elim_bvars;E.flex_addk_simplify_bools!_simplify_bools;ET.setup()in{Extensions.defaultwithExtensions.name="bool";env_actions=[register];}let()=Options.add_opts["--boolean-reasoning",Arg.Symbol(["off";"no-cases";"cases-inf";"cases-simpl";"cases-simpl-kp";"cases-eager";"cases-preprocess"],funs->_bool_reasoning:=matchswith|"off"->BoolReasoningDisabled|"no-cases"->BoolCasesDisabled|"cases-inf"->BoolCasesInference|"cases-simpl"->BoolCasesSimplification|"cases-simpl-kp"->BoolCasesKeepParent|"cases-preprocess"->BoolCasesPreprocess|_->assertfalse)," enable/disable boolean axioms";"--bool-subterm-selection",Arg.Symbol(["A";"M";"L"],(funopt->_cased_term_selection:=matchoptwith"A"->Any|"M"->Minimal|"L"->Large|_->assertfalse))," select boolean subterm selection criterion: A for any, M for minimal and L for large";"--quantifier-renaming",Arg.Bool(funv->_quant_rename:=v)," turn the quantifier renaming on or off";"--disable-simplifying-cnf",Arg.Set_cnf_non_simpl," implement cnf on-the-fly as an inference rule";"--interpret-bool-funs",Arg.Bool(funv->_interpret_bool_funs:=v)," turn interpretation of boolean functions as forall or negation of forall on or off";"--normalize-bool-terms",Arg.Bool((funv->_norm_bools:=v))," normalize boolean subterms using their weight.";"--nnf-nested-formulas",Arg.Bool(funv->_nnf:=v)," convert nested formulas into negation normal form";"--simplify-bools",Arg.Bool(funv->_simplify_bools:=v)," simplify boolean subterms";"--elim-bvars",Arg.Bool((:=)_elim_bvars)," replace boolean variables by T and F";"--boolean-reasoning-filter-literals",Arg.Symbol(["all";"max"],(funv->matchvwith|"all"->_filter_literals:=`All|"max"->_filter_literals:=`Max|_->assertfalse;))," select on which literals to apply bool reasoning rules"];Params.add_to_modes["ho-complete-basic";"ho-pragmatic";"lambda-free-intensional";"lambda-free-purify-intensional";"lambda-free-extensional";"ho-comb-complete";"lambda-free-purify-extensional";"fo-complete-basic"](fun()->_bool_reasoning:=BoolReasoningDisabled);Extensions.registerextension