1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281(* This file is free software, part of Zipperposition. See file "license" for more details. *)(** {1 Cancellative Inferences} *)openLogtkopenLibzipperpositionmoduleT=TermmoduleLit=LiteralmoduleLits=LiteralsmoduleS=SubstmoduleM=MonomemoduleMF=Monome.FocusmoduleAL=Int_litmoduleALF=AL.FocusmoduleStmt=StatementmoduleUS=Unif_substletstat_arith_sup=Util.mk_stat"int.superposition"letstat_arith_cancellation=Util.mk_stat"int.arith_cancellation"letstat_arith_eq_factoring=Util.mk_stat"int.eq_factoring"letstat_arith_ineq_chaining=Util.mk_stat"int.ineq_chaining"letstat_arith_case_switch=Util.mk_stat"int.case_switch"letstat_arith_semantic_tautology=Util.mk_stat"int.semantic_tauto"letstat_arith_semantic_tautology_steps=Util.mk_stat"int.semantic_tauto.steps"letstat_arith_ineq_factoring=Util.mk_stat"int.ineq_factoring"letstat_arith_div_chaining=Util.mk_stat"int.div_chaining"letstat_arith_divisibility=Util.mk_stat"int.divisibility"letstat_arith_demod=Util.mk_stat"int.demod"letstat_arith_backward_demod=Util.mk_stat"int.backward_demod"letstat_arith_trivial_ineq=Util.mk_stat"int.redundant_by_ineq.calls"letstat_arith_trivial_ineq_steps=Util.mk_stat"int.redundant_by_ineq.steps"letstat_arith_demod_ineq=Util.mk_stat"int.demod_ineq.calls"letstat_arith_demod_ineq_steps=Util.mk_stat"int.demod_ineq.steps"(*
let stat_arith_reflexivity_resolution = Util.mk_stat "int.reflexivity_resolution"
*)letprof_arith_sup=Util.mk_profiler"int.superposition"letprof_arith_cancellation=Util.mk_profiler"int.arith_cancellation"letprof_arith_eq_factoring=Util.mk_profiler"int.eq_factoring"letprof_arith_ineq_chaining=Util.mk_profiler"int.ineq_chaining"letprof_arith_demod=Util.mk_profiler"int.demod"letprof_arith_backward_demod=Util.mk_profiler"int.backward_demod"letprof_arith_semantic_tautology=Util.mk_profiler"int.semantic_tauto"letprof_arith_ineq_factoring=Util.mk_profiler"int.ineq_factoring"letprof_arith_div_chaining=Util.mk_profiler"int.div_chaining"letprof_arith_divisibility=Util.mk_profiler"int.divisibility"letprof_arith_trivial_ineq=Util.mk_profiler"int.redundant_by_ineq"letprof_arith_demod_ineq=Util.mk_profiler"int.demod_ineq"(*
let prof_arith_reflexivity_resolution = Util.mk_profiler "int.reflexivity_resolution"
*)letsection=Util.Section.make~parent:Const.section"int-arith"moduletypeS=sigmoduleEnv:Env.SmoduleC:moduletypeofEnv.CmodulePS:moduletypeofEnv.ProofState(** {3 Equations and Inequations} *)valcanc_sup_active:Env.binary_inf_rule(** cancellative superposition where given clause is active *)valcanc_sup_passive:Env.binary_inf_rule(** cancellative superposition where given clause is passive *)valcancellation:Env.unary_inf_rule(** cancellation (unifies some terms on both sides of a
comparison operator) *)valcanc_equality_factoring:Env.unary_inf_rule(** cancellative equality factoring *)valcanc_ineq_chaining:Env.binary_inf_rule(** cancellative inequality chaining.
Also does case switch if conditions are present:
C1 or a < b C2 or b < c
-------------------------------------
C1 or C2 or or_{i=a+1....c-1} (b = i)
if a and c are integer linear expressions whose difference is
a constant. If a > c, then the range a...c is empty and the literal
is just removed. *)valcanc_ineq_factoring:Env.unary_inf_rule(** Factoring between two inequation literals *)valcanc_less_to_lesseq:Env.lit_rewrite_rule(** Simplification: a <= b ----> a < b+1 *)(** {3 Divisibility} *)valcanc_div_chaining:Env.binary_inf_rule(** Chain together two divisibility literals, assuming they share the
same prime *)valcanc_div_case_switch:Env.unary_inf_rule(** Eliminate negative divisibility literals within a power-of-prime
quotient of Z:
not (d^i | m) -----> *)valcanc_div_prime_decomposition:Env.multi_simpl_rule(** Eliminate divisibility literals with a non-power-of-prime
quotient of Z (for instance [6 | a ---> { 2 | a, 3 | a }]) *)valcanc_divisibility:Env.unary_inf_rule(** Infer divisibility constraints from integer equations,
for instance C or 2a=b ----> C or 2 | b if a is maximal *)(** {3 Other} *)valis_tautology:C.t->bool(** is the clause a tautology w.r.t linear expressions? *)valeliminate_unshielded:Env.multi_simpl_rule(** Eliminate unshielded variables using an adaptation of
Cooper's algorithm *)(** {2 Contributions to Env} *)valregister:unit->unitendletenable_arith_=reftrueletenable_ac_=reffalseletenable_semantic_tauto_=reftrueletenable_trivial_ineq_=reftrueletenable_demod_ineq_=reftrueletdot_unit_=refNoneletdiff_to_lesseq_=ref`Simplifyletcase_switch_limit=ref30letdiv_case_switch_limit=ref100letflag_tauto=SClause.new_flag()letflag_computed_tauto=SClause.new_flag()moduleMake(E:Env.S):SwithmoduleEnv=E=structmoduleEnv=EmoduleCtx=Env.CtxmoduleC=Env.CmodulePS=Env.ProofStatelet_idx_eq=ref(PS.TermIndex.empty())let_idx_ineq_left=ref(PS.TermIndex.empty())let_idx_ineq_right=ref(PS.TermIndex.empty())let_idx_div=ref(PS.TermIndex.empty())let_idx_all=ref(PS.TermIndex.empty())(* unit clauses *)let_idx_unit_eq=ref(PS.TermIndex.empty())let_idx_unit_div=ref(PS.TermIndex.empty())let_idx_unit_ineq=ref(PS.TermIndex.empty())(* apply [f] to some subterms of [c] *)letupdatefc=letord=Ctx.ord()in_idx_eq:=Lits.fold_terms~vars:false~ty_args:false~which:`Max~ord~subterms:false~eligible:C.Eligible.(filterLit.is_arith_eqn**maxc)(C.litsc)|>Iter.fold(funacc(t,pos)->letwith_pos=C.WithPos.({term=t;pos;clause=c})infacctwith_pos)!_idx_eq;letleft,right=Lits.fold_terms~vars:false~ty_args:false~which:`Max~ord~subterms:false~eligible:C.Eligible.(filterLit.is_arith_ineq**maxc)(C.litsc)|>Iter.fold(fun(left,right)(t,pos)->letwith_pos=C.WithPos.({term=t;pos;clause=c})inmatchposwith|Position.Arg(_,Position.Left_)->flefttwith_pos,right|Position.Arg(_,Position.Right_)->left,frighttwith_pos|_->assertfalse)(!_idx_ineq_left,!_idx_ineq_right)in_idx_ineq_left:=left;_idx_ineq_right:=right;_idx_div:=Lits.fold_terms~vars:false~ty_args:false~which:`Max~ord~subterms:false~eligible:C.Eligible.(filterLit.is_arith_divides**maxc)(C.litsc)|>Iter.fold(funacc(t,pos)->letwith_pos=C.WithPos.({term=t;pos;clause=c})infacctwith_pos)!_idx_div;_idx_all:=Lits.fold_terms~vars:false~ty_args:false~which:`Max~ord~subterms:false~eligible:C.Eligible.(filterLit.is_arith**maxc)(C.litsc)|>Iter.fold(funacc(t,pos)->letwith_pos=C.WithPos.({term=t;pos;clause=c})infacctwith_pos)!_idx_all;()(* simplification set *)letupdate_simplfc=letord=Ctx.ord()inbeginmatchC.litscwith|[|Lit.Int((AL.Binary(AL.Equal,_,_))asalit)|]->letpos=Position.(arg0stop)in_idx_unit_eq:=AL.fold_terms~subterms:false~vars:false~pos~which:`Max~ordalit|>Iter.fold(funacc(t,pos)->assert(not(T.is_vart));letwith_pos=C.WithPos.({term=t;pos;clause=c;})infacctwith_pos)!_idx_unit_eq|[|Lit.Int((AL.Binary(AL.Lesseq,_,_))asalit)|]->letpos=Position.(arg0stop)in_idx_unit_ineq:=if!enable_trivial_ineq_||!enable_demod_ineq_thenAL.fold_terms~subterms:false~vars:false~pos~which:`Max~ordalit|>Iter.fold(funacc(t,pos)->assert(not(T.is_vart));letwith_pos=C.WithPos.({term=t;pos;clause=c;})infacctwith_pos)!_idx_unit_ineqelse!_idx_unit_ineq|[|Lit.Int(AL.Dividesdasalit)|]whend.AL.sign->letpos=Position.(arg0stop)in_idx_unit_div:=AL.fold_terms~subterms:false~vars:false~pos~which:`Max~ordalit|>Iter.fold(funacc(t,pos)->assert(not(T.is_vart));letwith_pos=C.WithPos.({term=t;pos;clause=c;})infacctwith_pos)!_idx_unit_div|_->()end;()let()=Signal.onPS.ActiveSet.on_add_clause(func->if!enable_arith_thenupdatePS.TermIndex.addc;Signal.ContinueListening);Signal.onPS.SimplSet.on_add_clause(func->if!enable_arith_thenupdate_simplPS.TermIndex.addc;Signal.ContinueListening);Signal.onPS.ActiveSet.on_remove_clause(func->if!enable_arith_thenupdatePS.TermIndex.removec;Signal.ContinueListening);Signal.onPS.SimplSet.on_remove_clause(func->if!enable_arith_thenupdate_simplPS.TermIndex.removec;Signal.ContinueListening);()(** {2 Utils} *)(* data required for superposition *)moduleSupInfo=structtypet={active:C.t;active_pos:Position.t;active_lit:AL.Focus.t;active_scope:int;passive:C.t;passive_pos:Position.t;passive_lit:AL.Focus.t;passive_scope:int;subst:US.t;}endletrule_canc=Proof.Rule.mk"canc_sup"(* do cancellative superposition *)let_do_cancinfoacc=letopenSupInfoinletord=Ctx.ord()inletrenaming=Subst.Renaming.create()inletus=info.substinletsubst=US.substusinletidx_a,_=Lits.Pos.cutinfo.active_posinletidx_p,_=Lits.Pos.cutinfo.passive_posinlets_a=info.active_scopeands_p=info.passive_scopeinletlit_a=ALF.apply_substrenamingsubst(info.active_lit,s_a)inletlit_p=ALF.apply_substrenamingsubst(info.passive_lit,s_p)inUtil.debugf~section5"@[<2>arith superposition@ between @[%a[%d]@]@ and @[%a[%d]@]@ (subst @[%a@])...@]"(funk->kC.ppinfo.actives_aC.ppinfo.passives_pSubst.ppsubst);(* check ordering conditions *)ifC.is_maxlit(info.active,s_a)subst~idx:idx_a&&C.is_maxlit(info.passive,s_p)subst~idx:idx_p&&ALF.is_max~ordlit_a(* && ALF.is_max ~ord lit_p *)then((* the active literals *)letlit_a,lit_p=ALF.scalelit_alit_pin(* other literals *)letlits_a=CCArray.except_idx(C.litsinfo.active)idx_ainletlits_a=Lit.apply_subst_listrenamingsubst(lits_a,s_a)inletlits_p=CCArray.except_idx(C.litsinfo.passive)idx_pinletlits_p=Lit.apply_subst_listrenamingsubst(lits_p,s_p)inletc_guard=Lit.of_unif_substrenamingusin(* new literal: lit_a=[t+m1=m2], lit_p=[t'+m1' R m2'] for some
relation R. Now let's replace t' by [m2-m1] in lit', ie,
build m = [m1'-m2'+(m2-m1) R 0]. *)letmf_a,m_a=matchlit_awith|ALF.Left(AL.Equal,mf,m)|ALF.Right(AL.Equal,m,mf)->mf,m|_->assertfalseinletnew_lit=matchlit_pwith|ALF.Left(op,mf_p,m_p)->Lit.mk_arith_opop(M.sum(MF.restmf_p)m_a)(M.summ_p(MF.restmf_a))|ALF.Right(op,m_p,mf_p)->Lit.mk_arith_opop(M.summ_p(MF.restmf_a))(M.sum(MF.restmf_p)m_a)|ALF.Div_->Lit.mk_arith(ALF.replacelit_p(M.differencem_a(MF.restmf_a)))inletall_lits=new_lit::c_guard@lits_a@lits_pin(* build clause *)letproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:rule_canc[C.proof_parent_substrenaming(info.active,s_a)subst;C.proof_parent_substrenaming(info.passive,s_p)subst]inlettrail=C.trail_l[info.active;info.passive]inletpenalty=C.penaltyinfo.active+C.penaltyinfo.passiveinletnew_c=C.create~penalty~trailall_litsproofinUtil.debugf~section5"@[<2>... gives@ @[%a@]@]"(funk->kC.ppnew_c);Util.incr_statstat_arith_sup;new_c::acc)else(Util.debug~section5"... has bad ordering conditions";acc)letcanc_sup_activec=Util.enter_profprof_arith_sup;letord=Ctx.ord()inleteligible=C.Eligible.(pos**maxc**filterLit.is_arith_eq)inletsc_a=0andsc_p=1inletres=Lits.fold_arith_terms~eligible~which:`Max~ord(C.litsc)|>Iter.fold(funacc(t,active_lit,active_pos)->assert(ALF.opactive_lit=`BinaryAL.Equal);Util.debugf~section5"@[<2>active canc. sup.@ with @[%a@]@ in @[%a@]@]"(funk->kALF.ppactive_litC.ppc);PS.TermIndex.retrieve_unifiables(!_idx_all,sc_p)(t,sc_a)|>Iter.fold(funacc(t',with_pos,subst)->letpassive=with_pos.C.WithPos.clauseinletpassive_pos=with_pos.C.WithPos.posinletpassive_lit=Lits.View.get_arith_exn(C.litspassive)passive_posinUtil.debugf~section5"@[<4> possible match:@ @[%a@]@ in @[%a@]@]"(funk->kALF.pppassive_litC.pppassive);(* now to unify active_lit and passive_lit further *)ifT.is_vart||T.is_vart'thenaccelseALF.unify~subst(active_lit,sc_a)(passive_lit,sc_p)|>Iter.fold(funacc(active_lit,passive_lit,subst)->letinfo=SupInfo.({active=c;active_pos;active_lit;active_scope=sc_a;passive;passive_pos;passive_lit;passive_scope=sc_p;subst;})in_do_cancinfoacc)acc)acc)[]inUtil.exit_profprof_arith_sup;resletcanc_sup_passivec=Util.enter_profprof_arith_sup;letord=Ctx.ord()inleteligible=C.Eligible.(maxc**arith)inletsc_a=0andsc_p=1inletres=Lits.fold_arith_terms~eligible~which:`All~ord(C.litsc)|>Iter.fold(funacc(t,passive_lit,passive_pos)->Util.debugf~section5"@[<2>passive canc. sup.@ with @[%a@]@ in @[%a@]@]"(funk->kALF.pppassive_litC.ppc);PS.TermIndex.retrieve_unifiables(!_idx_eq,sc_a)(t,sc_p)|>Iter.fold(funacc(t',with_pos,subst)->letactive=with_pos.C.WithPos.clauseinletactive_pos=with_pos.C.WithPos.posinletactive_lit=Lits.View.get_arith_exn(C.litsactive)active_posin(* must have an equation as active lit *)matchALF.opactive_litwith|`BinaryAL.Equalwhennot(T.is_vart)&¬(T.is_vart')->Util.debugf~section5"@[<4> possible match:@ @[%a@]@ in @[%a@]@]"(funk->kALF.pppassive_litC.ppc);(* unify literals further *)ALF.unify~subst(active_lit,sc_a)(passive_lit,sc_p)|>Iter.fold(funacc(active_lit,passive_lit,subst)->letinfo=SupInfo.({active;active_pos;active_lit;active_scope=sc_a;passive=c;passive_pos;passive_lit;passive_scope=sc_p;subst;})in_do_cancinfoacc)acc|_->acc)acc)[]inUtil.exit_profprof_arith_sup;resexceptionSimplifyIntoofAL.t*C.t*S.t(* how to simplify the passive lit with the active lit, in one step *)let_try_demod_step~substpassive_lit_s_pcposactive_lits_ac'_pos'=letord=Ctx.ord()inleti=Lits.Pos.idxposinletrenaming=S.Renaming.create()inletactive_lit'=ALF.apply_substrenamingsubst(active_lit,s_a)in(* restrictions:
- the rewriting term must be bigger than other terms
(in other words, the inference is strictly decreasing)
- all variables of active clause must be bound by subst
- must not rewrite itself (c != c')
- trail(active) must subsume trail(passive) *)ifALF.is_strictly_max~ordactive_lit'&&(C.Seq.varsc'|>Iter.for_all(funv->S.memsubst((v:Type.tHVar.t:>InnerTerm.tHVar.t),s_a)))&&((C.litsc|>Array.length)>1||not(Lit.equal(C.litsc).(i)(C.litsc').(0))||not(ALF.is_max~ordpassive_lit&&C.is_maxlit(c,0)S.empty~idx:i))&&(C.trail_subsumesc'c)then((* we know all variables of [active_lit] are bound, no need
for a renaming *)letactive_lit=ALF.apply_substSubst.Renaming.nonesubst(active_lit,s_a)inletactive_lit,passive_lit=ALF.scaleactive_litpassive_litinmatchactive_lit,passive_litwith|ALF.Left(AL.Equal,mf1,m1),_|ALF.Right(AL.Equal,m1,mf1),_->letnew_lit=ALF.replacepassive_lit(M.differencem1(MF.restmf1))inraise(SimplifyInto(new_lit,c',subst))|ALF.Divd1,ALF.Divd2whend1.AL.sign->letn1=Z.powd1.AL.numd1.AL.powerandn2=Z.powd2.AL.numd2.AL.powerinletgcd=Z.gcd(MF.coeffd1.AL.monome)(MF.coeffd2.AL.monome)in(* simplification: we only do the rewriting if both
literals have exactly the same num and power...
TODO: generalize *)ifZ.equaln1n2&&Z.ltgcdZ.(powd2.AL.numd2.AL.power)thenletnew_lit=ALF.replacepassive_lit(M.uminus(MF.restd1.AL.monome))inraise(SimplifyInto(new_lit,c',subst))|_->())else()(* reduce an arithmetic literal to its current normal form *)letrec_demod_lit_nf~add_lit~add_premise~ica_lit=letord=Ctx.ord()inlets_a=1ands_p=0in(* scopes *)(* which term indexes can be used *)letindexes=matcha_litwith|AL.Divides_->[!_idx_unit_div;!_idx_unit_eq]|AL.Binary_->[!_idx_unit_eq]inbegintryAL.fold_terms~pos:Position.stop~vars:false~which:`Max~ord~subterms:falsea_lit|>Iter.iter(fun(t,lit_pos)->assert(not(T.is_vart));letpassive_lit=ALF.get_exna_litlit_posin(* search for generalizations of [t] *)List.iter(funindex->PS.TermIndex.retrieve_generalizations(index,s_a)(t,s_p)|>Iter.iter(fun(_t',with_pos,subst)->letc'=with_pos.C.WithPos.clauseinletpos'=with_pos.C.WithPos.posinassert(C.is_unit_clausec');assert(Lits.Pos.idxpos'=0);letactive_lit=Lits.View.get_arith_exn(C.litsc')pos'inletpos=Position.(argilit_pos)in_try_demod_step~substpassive_lits_pcposactive_lits_ac'pos'))indexes);(* could not simplify, keep the literal *)add_lit(Lit.mk_aritha_lit)withSimplifyInto(a_lit',c',subst)->(* lit ----> lit' *)add_premisec'subst;(* recurse until the literal isn't reducible *)Util.debugf~section4"@[<2>rewrite arith lit (@[%a@])@ into (@[%a@])@ using clause @[%a@]@ and subst @[%a@]@]"(funk->kAL.ppa_litAL.ppa_lit'C.ppc'S.ppsubst);_demod_lit_nf~add_premise~add_lit~ica_lit'endleteq_c_subst(c1,s1)(c2,s2)=C.equalc1c2&&Subst.equals1s2(* demodulation (simplification) *)let_demodulationc=Util.enter_profprof_arith_demod;letdid_simplify=reffalseinletlits=ref[]in(* simplified literals *)letadd_litl=lits:=l::!litsinletclauses=ref[]in(* simplifying clauses *)(* add a rewriting clause *)letadd_premisec'subst=did_simplify:=true;clauses:=(c',subst)::!clausesin(* simplify each and every literal *)Lits.fold_lits~eligible:C.Eligible.always(C.litsc)|>Iter.iter(fun(lit,i)->matchlitwith|Lit.Inta_lit->_demod_lit_nf~add_lit~add_premise~ica_lit|_->add_litlit(* keep non-arith literals *));(* build result clause (if it was simplified) *)letres=if!did_simplifythen(clauses:=CCList.uniq~eq:eq_c_subst!clauses;letproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"canc_demod")(C.proof_parentc::List.rev_map(fun(c,subst)->C.proof_parent_substSubst.Renaming.none(c,1)subst)!clauses)inlettrail=C.trailcinletnew_c=C.create~penalty:(C.penaltyc)~trail(List.rev!lits)proofinUtil.incr_statstat_arith_demod;Util.debugf~section5"@[<2>arith demodulation@ of @[%a@]@ with [@[%a@]]@ gives @[%a@]@]"(funk->letpp_c_sout(c,s)=Format.fprintfout"(@[%a@ :subst %a@])"C.ppcSubst.ppsinkC.ppc(Util.pp_listpp_c_s)!clausesC.ppnew_c);SimplM.return_newnew_c)elseSimplM.return_samecinUtil.exit_profprof_arith_demod;resletcanc_demodulationc=_demodulationc(* find clauses in which some literal could be rewritten by [c], iff
[c] is a positive unit arith clause *)letcanc_backward_demodulationc=Util.enter_profprof_arith_backward_demod;letord=Ctx.ord()inletres=C.ClauseSet.emptyinletres=matchC.litscwith|[|Lit.Int(AL.Binary(AL.Equal,_,_)asalit)|]->AL.fold_terms~vars:false~which:`Max~subterms:false~ordalit|>Iter.fold(funacc(t,pos)->PS.TermIndex.retrieve_specializations(!_idx_all,0)(t,1)|>Iter.fold(funacc(_t',with_pos,subst)->letc'=with_pos.C.WithPos.clausein(* check whether the term [t] is indeed maximal in
its literal (and clause) after substitution *)letalit'=ALF.get_exnalitposinletalit'=ALF.apply_substSubst.Renaming.nonesubst(alit',1)inifC.trail_subsumesc'c&&ALF.is_max~ordalit'then(Util.incr_statstat_arith_backward_demod;C.ClauseSet.addc'acc)elseacc)acc)C.ClauseSet.empty|[|Lit.Int(AL.Binary(AL.Lesseq,_m1,_m2))|]->res(* TODO *)|[|Lit.Int(AL.Dividesd)|]whend.AL.sign->res(* TODO *)|_->res(* no demod *)inUtil.exit_profprof_arith_backward_demod;resletcancellationc=Util.enter_profprof_arith_cancellation;letord=Ctx.ord()inleteligible=C.Eligible.(maxc**arith)inletres=Lits.fold_arith~eligible(C.litsc)|>Iter.fold(funacc(a_lit,pos)->letidx=Lits.Pos.idxposin(* cancellation depends on what the literal looks like *)matcha_litwith|AL.Binary(op,m1,m2)->Util.debugf~section5"@[<2>try cancellation@ in @[%a@]@]"(funk->kAL.ppa_lit);(* try to unify terms in [m1] and [m2] *)MF.unify_mm(m1,0)(m2,0)|>Iter.fold(funacc(mf1,mf2,us)->letrenaming=Subst.Renaming.create()inletsubst=US.substusinletmf1'=MF.apply_substrenamingsubst(mf1,0)inletmf2'=MF.apply_substrenamingsubst(mf2,0)inletis_max_lit=C.is_maxlit(c,0)subst~idxinUtil.debugf~section5"@[<4>... candidate:@ @[%a@] (max lit ? %B)@ :mf1 %a@ :mf2 %a@]"(funk->kS.ppsubstis_max_litMF.ppmf1'MF.ppmf2');ifis_max_lit&&MF.is_max~ordmf1'&&MF.is_max~ordmf2'then((* do the inference *)letlits'=CCArray.except_idx(C.litsc)idxinletlits'=Lit.apply_subst_listrenamingsubst(lits',0)inletnew_lit=Lit.mk_arith_opop(MF.restmf1')(MF.restmf2')inletc_guard=Literal.of_unif_substrenamingusinletall_lits=new_lit::c_guard@lits'inletproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"cancellation")[C.proof_parent_substrenaming(c,0)subst]inlettrail=C.trailcinletpenalty=C.penaltycinletnew_c=C.create~trail~penaltyall_litsproofinUtil.debugf~section3"@[<2>cancellation@ of @[%a@]@ (with %a)@ into @[%a@]@]"(funk->kC.ppcSubst.ppsubstC.ppnew_c);Util.incr_statstat_arith_cancellation;new_c::acc)elseacc)acc|AL.Dividesd->Util.debugf~section5"@[try cancellation@ in @[%a@]@]"(funk->kAL.ppa_lit);MF.unify_self_monome(d.AL.monome,0)|>Iter.fold(funacc(mf,us)->letrenaming=Subst.Renaming.create()inletsubst=US.substusinletmf'=MF.apply_substrenamingsubst(mf,0)inifC.is_maxlit(c,0)subst~idx&&MF.is_max~ordmfthen(letlits'=CCArray.except_idx(C.litsc)idxinletlits'=Lit.apply_subst_listrenamingsubst(lits',0)inletnew_lit=Lit.mk_divides~sign:d.AL.signd.AL.num~power:d.AL.power(MF.to_monomemf')inletc_guard=Literal.of_unif_substrenamingusinletall_lits=new_lit::c_guard@lits'inletproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"cancellation")[C.proof_parent_substrenaming(c,0)subst]inlettrail=C.trailcandpenalty=C.penaltycinletnew_c=C.create~trail~penaltyall_litsproofinUtil.debugf~section3"@[<2>cancellation@ of @[%a@]@ (with %a)@ into @[%a@]@]"(funk->kC.ppcSubst.ppsubstC.ppnew_c);Util.incr_statstat_arith_cancellation;new_c::acc)elseacc)acc)[]inUtil.exit_profprof_arith_cancellation;resletrule_canc_eq_fact=Proof.Rule.mk"arith_eq_factoring"letcanc_equality_factoringc=Util.enter_profprof_arith_eq_factoring;letord=Ctx.ord()inleteligible=C.Eligible.(maxc**filterLit.is_arith_eq)inletres=Lits.fold_arith_terms~which:`Max~eligible~ord(C.litsc)|>Iter.fold(funacc(t1,lit1,pos1)->assert(ALF.oplit1=`BinaryAL.Equal);letidx1=Lits.Pos.idxpos1in(* lit1 is the factored literal *)Lits.fold_arith_terms~which:`Max~ord~eligible:C.Eligible.(filterLit.is_arith_eq)(C.litsc)|>Iter.fold(funacc(t2,lit2,pos2)->assert(ALF.oplit2=`BinaryAL.Equal);letidx2=Lits.Pos.idxpos2inletmf1=ALF.focused_monomelit1andmf2=ALF.focused_monomelit2intryifidx1=idx2thenraiseUnif.Fail;(* exit *)letsubst=Unif.FO.unify_full(t1,0)(t2,0)inUtil.debugf~section5"@[<2>arith canc. eq. factoring:@ possible match in @[%a@]@ (at %d, %d)@]"(funk->kC.ppcidx1idx2);MF.unify_ff~subst(mf1,0)(mf2,0)|>Iter.fold(funacc(_,_,us)->letrenaming=Subst.Renaming.create()inletsubst=US.substusinletlit1'=ALF.apply_substrenamingsubst(lit1,0)inletlit2'=ALF.apply_substrenamingsubst(lit2,0)inifC.is_maxlit(c,0)subst~idx:idx1&&ALF.is_max~ordlit1'&&ALF.is_max~ordlit2'then((* lit1 is a.t + l1 = l2,
lit2 is a'.t + l1' = l2',
so we scale them, and replace lit1 with
a'.l1 + a.l2' != a'.l2 + a.l1' *)letlit1',lit2'=ALF.scalelit1'lit2'inletm1=ALF.opposite_monome_exnlit1'andmf1=ALF.focused_monomelit1'andm2=ALF.opposite_monome_exnlit2'andmf2=ALF.focused_monomelit2'inletnew_lit=Lit.mk_arith_neq(M.summ1(MF.restmf2))(M.summ2(MF.restmf1))inletother_lits=CCArray.except_idx(C.litsc)idx1inletother_lits=Lit.apply_subst_listrenamingsubst(other_lits,0)inletc_guard=Literal.of_unif_substrenamingusin(* apply subst and build clause *)letall_lits=new_lit::c_guard@other_litsinletproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:rule_canc_eq_fact[C.proof_parent_substrenaming(c,0)subst]inletpenalty=C.penaltycandtrail=C.trailcinletnew_c=C.create~trail~penaltyall_litsproofinUtil.debugf~section5"@[<2>arith_eq_factoring:@ @[%a@]@ gives @[%a@]@]"(funk->kC.ppcC.ppnew_c);Util.incr_statstat_arith_eq_factoring;new_c::acc)elseacc)accwithUnif.Fail->acc)acc)[]inUtil.exit_profprof_arith_eq_factoring;res(** Data necessary to fully describe a chaining inference.
[left] is basically the clause/literal in which the chained
term is on the left of <,
[right] is the other one. *)moduleChainingInfo=structtypet={left:C.t;left_scope:int;left_pos:Position.t;left_lit:AL.Focus.t;right:C.t;right_scope:int;right_pos:Position.t;right_lit:AL.Focus.t;subst:US.t;}end(* range from low to low+len *)let_rangelowlen=letrecmakeaccilen=ifZ.signlen<0thenaccelsemake(i::acc)(Z.succi)(Z.predlen)inmake[]lowlen(* cancellative chaining *)let_do_chaininginfoacc=letopenChainingInfoinletord=Ctx.ord()inletrenaming=S.Renaming.create()inletus=info.substinletsubst=US.substusinletidx_l,_=Lits.Pos.cutinfo.left_posinletidx_r,_=Lits.Pos.cutinfo.right_posinlets_l=info.left_scopeands_r=info.right_scopeinletlit_l=ALF.apply_substrenamingsubst(info.left_lit,s_l)inletlit_r=ALF.apply_substrenamingsubst(info.right_lit,s_r)inUtil.debugf~section5"@[<2>arith chaining@ between @[%a[%d]@]@ and @[%a[%d]@]@ (subst @[%a@])...@]"(funk->kC.ppinfo.lefts_lC.ppinfo.rights_rSubst.ppsubst);(* check ordering conditions *)ifC.is_maxlit(info.left,s_l)subst~idx:idx_l&&C.is_maxlit(info.right,s_r)subst~idx:idx_r&&ALF.is_max~ordlit_l&&ALF.is_max~ordlit_rthen((* scale literals *)letlit_l,lit_r=ALF.scalelit_llit_rinmatchlit_l,lit_rwith|ALF.Left(AL.Lesseq,mf_1,m1),ALF.Right(AL.Lesseq,m2,mf_2)->(* m2 ≤ mf_2 and mf_1 ≤ m1, with mf_1 and mf_2 sharing the same
focused term. We deduce m2 + mf_1 ≤ m1 + mf_2 and cancel the
term out (after scaling) *)assert(Z.equal(MF.coeffmf_1)(MF.coeffmf_2));letnew_lit=Lit.mk_arith_lesseq(M.summ2(MF.restmf_1))(M.summ1(MF.restmf_2))inletlits_l=CCArray.except_idx(C.litsinfo.left)idx_linletlits_l=Lit.apply_subst_listrenamingsubst(lits_l,s_l)inletlits_r=CCArray.except_idx(C.litsinfo.right)idx_rinletlits_r=Lit.apply_subst_listrenamingsubst(lits_r,s_r)inletc_guard=Literal.of_unif_substrenamingusinletall_lits=new_lit::c_guard@lits_l@lits_rinletproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"canc_ineq_chaining")[C.proof_parent_substrenaming(info.left,s_l)subst;C.proof_parent_substrenaming(info.right,s_r)subst]inlettrail=C.trail_l[info.left;info.right]in(* penalty for some chaining *)letpenalty=C.penaltyinfo.left+C.penaltyinfo.right+3(* nested chainings are dangerous *)+(ifMF.termmf_1|>T.is_varthen10else0)+(ifMF.termmf_2|>T.is_varthen10else0)inletnew_c=C.create~penalty~trailall_litsproofinUtil.debugf~section5"@[<2>ineq chaining@ of @[%a@]@ and @[%a@]@ gives @[%a@]@]"(funk->kC.ppinfo.leftC.ppinfo.rightC.ppnew_c);Util.incr_statstat_arith_ineq_chaining;letacc=new_c::accin(* now, maybe we can also perform case switch! We can if
mf_1 - m1 = k + (m2 - mf_2). In this case necessarily
Or_{i=0...k} mf_2 = m2 + i *)letdiff=M.difference(M.summ1(MF.restmf_2))(M.summ2(MF.restmf_1))inifM.is_constdiff&&Z.leq(M.constdiff)Z.(of_int!case_switch_limit)then((* re-use lits_l and lits_r, but build an enumeration *)letnew_lits=List.map(funi->(* mf_2 = m2 + i *)Lit.mk_arith_eq(MF.to_monomemf_2)(M.add_constm2i))(_rangeZ.zero(M.constdiff))inletall_lits=CCList.flatten[new_lits;c_guard;lits_l;lits_r]inletproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"canc_case_switch")[C.proof_parent_substrenaming(info.left,s_l)subst;C.proof_parent_substrenaming(info.right,s_r)subst]inlettrail=C.trail_l[info.left;info.right]in(* small penalty for case switch *)letpenalty=C.penaltyinfo.left+C.penaltyinfo.right+3inletnew_c=C.create~trail~penaltyall_litsproofinUtil.debugf~section5"@[<2>case switch@ of @[%a@]@ and @[%a@]@ gives @[%a@]@]"(funk->kC.ppinfo.leftC.ppinfo.rightC.ppnew_c);Util.incr_statstat_arith_case_switch;new_c::acc)elseacc|_->assertfalse)elseaccletcanc_ineq_chainingc=Util.enter_profprof_arith_ineq_chaining;letord=Ctx.ord()inleteligible=C.Eligible.(maxc**filterLit.is_arith_lesseq)inletsc_l=0andsc_r=1inletres=Lits.fold_arith_terms~eligible~ord~which:`Max(C.litsc)|>Iter.fold(funacc(t,lit,pos)->matchlitwith|_whenT.is_vart->acc(* ignore variables *)|ALF.Left(AL.Lesseq,mf_l,_)->(* find a right-chaining literal in some other clause *)PS.TermIndex.retrieve_unifiables(!_idx_ineq_right,sc_r)(t,sc_l)|>Iter.fold(funacc(_t',with_pos,subst)->letright=with_pos.C.WithPos.clauseinletright_pos=with_pos.C.WithPos.posinletlit_r=Lits.View.get_arith_exn(C.litsright)right_posinmatchlit_rwith|ALF.Right(AL.Lesseq,_,mf_r)->MF.unify_ff~subst(mf_l,sc_l)(mf_r,sc_r)|>Iter.fold(funacc(_,_,subst)->letinfo=ChainingInfo.({left=c;left_scope=sc_l;left_lit=lit;left_pos=pos;right;right_scope=sc_r;right_lit=lit_r;right_pos;subst;})in_do_chaininginfoacc)acc|_->acc)acc|ALF.Right(AL.Lesseq,_,mf_r)->(* find a right-chaining literal in some other clause *)PS.TermIndex.retrieve_unifiables(!_idx_ineq_left,sc_l)(t,sc_r)|>Iter.fold(funacc(_t',with_pos,subst)->letleft=with_pos.C.WithPos.clauseinletleft_pos=with_pos.C.WithPos.posinletlit_l=Lits.View.get_arith_exn(C.litsleft)left_posinmatchlit_lwith|ALF.Left(AL.Lesseq,mf_l,_)->MF.unify_ff~subst(mf_l,sc_l)(mf_r,sc_r)|>Iter.fold(funacc(_,_,subst)->letinfo=ChainingInfo.({left;left_scope=sc_l;left_lit=lit_l;left_pos;subst;right=c;right_scope=sc_r;right_lit=lit;right_pos=pos;})in_do_chaininginfoacc)acc|_->acc)acc|_->assertfalse)[]inUtil.exit_profprof_arith_ineq_chaining;res(* TODO: update with equality case, check that signs correct *)letcanc_ineq_factoringc=Util.enter_profprof_arith_ineq_factoring;letord=Ctx.ord()inletacc=ref[]in(* do the factoring if ordering conditions are ok *)let_do_factoring~subst:uslit1lit2ij=letrenaming=S.Renaming.create()inletsubst=US.substusinletlit1=ALF.apply_substrenamingsubst(lit1,0)inletlit2=ALF.apply_substrenamingsubst(lit2,0)in(* same coefficient for the focused term *)letlit1,lit2=ALF.scalelit1lit2inmatchlit1,lit2with|ALF.Left(AL.Lesseq,mf1,m1),ALF.Left(AL.Lesseq,mf2,m2)|ALF.Right(AL.Lesseq,m1,mf1),ALF.Right(AL.Lesseq,m2,mf2)->(* mf1 ≤ m1 or mf2 ≤ m2 (symmetry with > if needed)
so we deduce that if m1-mf1.rest ≤ m2 - mf2.rest
then the first literal implies the second, so we only
keep the second one *)assert(Z.equal(MF.coeffmf1)(MF.coeffmf2));if(C.is_maxlit(c,0)subst~idx:i||C.is_maxlit(c,0)subst~idx:j)&&(ALF.is_max~ordlit1||ALF.is_max~ordlit2)then(letleft=matchlit1withALF.Left_->true|_->falsein(* remove lit1, add the guard *)letother_lits=CCArray.except_idx(C.litsc)iinletother_lits=Lit.apply_subst_listrenamingsubst(other_lits,0)in(* build new literal *)letnew_lit=ifleftthenLit.mk_arith_lesseq(M.differencem1(MF.restmf1))(M.differencem2(MF.restmf2))elseLit.mk_arith_lesseq(M.differencem2(MF.restmf2))(M.differencem1(MF.restmf1))in(* negate the literal to obtain a guard *)letnew_lit=Lit.negatenew_litinletc_guard=Literal.of_unif_substrenamingusinletlits=new_lit::c_guard@other_litsin(* build clauses *)letproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"canc_ineq_factoring")[C.proof_parent_substrenaming(c,0)subst]inlettrail=C.trailcandpenalty=C.penaltycinletnew_c=C.create~trail~penaltylitsproofinUtil.debugf~section5"@[<2>ineq factoring@ of @[%a@]@ gives @[%a@]@"(funk->kC.ppcC.ppnew_c);Util.incr_statstat_arith_ineq_factoring;acc:=new_c::!acc)|_->()in(* traverse the clause to find matching pairs *)leteligible=C.Eligible.(maxc**filterLit.is_arith_lesseq)inLits.fold_arith~eligible(C.litsc)|>Iter.iter(fun(lit1,pos1)->leti=Lits.Pos.idxpos1inleteligible'=C.Eligible.(filterLit.is_arith_lesseq)inLits.fold_arith~eligible:eligible'(C.litsc)|>Iter.iter(fun(lit2,pos2)->letj=Lits.Pos.idxpos2inmatchlit1,lit2with|_wheni=j->()(* need distinct lits *)|AL.Binary(AL.Lesseq,l1,r1),AL.Binary(AL.Lesseq,l2,r2)->(* see whether we have l1 < a.x + mf1 and l2 < a.x + mf2 *)MF.unify_mm(r1,0)(r2,0)(fun(mf1,mf2,subst)->letlit1=ALF.mk_rightAL.Lesseql1mf1inletlit2=ALF.mk_rightAL.Lesseql2mf2in_do_factoring~substlit1lit2ij);(* see whether we have a.x + mf1 < r1 and a.x + mf2 < r2 *)MF.unify_mm(l1,0)(l2,0)(fun(mf1,mf2,subst)->letlit1=ALF.mk_leftAL.Lesseqmf1r1inletlit2=ALF.mk_leftAL.Lesseqmf2r2in_do_factoring~substlit1lit2ij);()|_->assertfalse));Util.exit_profprof_arith_ineq_factoring;!acc(** One-shot literal/clause removal.
We use unit clauses to try to prove a literal absurd/tautological, possibly
using {b several} instances of unit clauses.
For instance, 0 ≤ f(x) makes 0 ≤ f(a) + f(b) redundant, but subsumption
is not able to detect it. *)(* allow traces of depth at most 3 *)letmax_ineq_trivial_steps=3(* rewrite a literal [l] into a smaller literal [l'], such that [l'] and
the current set of unit clauses imply [l]; then compute the
transitive closure of this relation. If we obtain a trivial
literal, then [l] is redundant (we keep a trace of literals used).
We use continuations to deal with the multiple choices. *)letrec_ineq_find_sufficient~ord~traceclitk=matchlitwith|_whenAL.is_triviallit->k(trace,lit)|_whenList.lengthtrace>=max_ineq_trivial_steps->()(* need another step, but it would exceed the limit *)|AL.Binary_whenIter.existsT.is_var(AL.Seq.termslit)->()(* no way we rewrite this into a tautology *)|AL.Binary(AL.Lesseq,_,_)->Util.incr_statstat_arith_trivial_ineq;Util.debugf~section5"(@[try_ineq_find_sufficient@ :lit `%a`@ :trace (@[%a@])@])"(funk->kAL.pplit(Util.pp_listC.pp)trace);AL.fold_terms~vars:false~which:`Max~ord~subterms:falselit|>Iter.iter(fun(t,pos)->letplit=ALF.get_exnlitposinletis_left=matchposwith|Position.Left_->true|Position.Right_->false|_->assertfalsein(* try to eliminate [t] in passive lit [plit]*)PS.TermIndex.retrieve_generalizations(!_idx_unit_ineq,1)(t,0)|>Iter.iter(fun(_t',with_pos,subst)->letactive_clause=with_pos.C.WithPos.clauseinletactive_pos=with_pos.C.WithPos.posinmatchLits.View.get_arith(C.litsactive_clause)active_poswith|None->assertfalse|Some(ALF.Left(AL.Lesseq,_,_)asalit')whenis_left->letalit'=ALF.apply_substSubst.Renaming.nonesubst(alit',1)inifC.trail_subsumesactive_clausec&&ALF.is_strictly_max~ordalit'then((* scale *)letplit,_alit'=ALF.scaleplitalit'inletmf1',m2'=matchLits.View.get_arith(C.litsactive_clause)active_poswith|Some(ALF.Left(_,mf1',m2'))->mf1',m2'|_->assertfalseinletmf1'=MF.apply_substSubst.Renaming.nonesubst(mf1',1)inletm2'=M.apply_substSubst.Renaming.nonesubst(m2',1)in(* from t+mf1 ≤ m2 and t+mf1' ≤ m2', we deduce
that if m2'-mf1' ≤ m2-mf1 then [lit] is redundant.
That is, the sufficient literal is
mf1 + m2' ≤ m2 + mf1' (we replace [t] with [m2'-mf1']) *)letnew_plit=ALF.replaceplit(M.differencem2'(MF.restmf1'))in(* transitive closure *)lettrace=active_clause::tracein_ineq_find_sufficient~ord~tracecnew_plitk)|Some(ALF.Right(AL.Lesseq,_,_)asalit')whennotis_left->(* symmetric case *)letalit'=ALF.apply_substSubst.Renaming.nonesubst(alit',1)inifC.trail_subsumesactive_clausec&&ALF.is_strictly_max~ordalit'then((* scale *)letplit,_alit'=ALF.scaleplitalit'inletm1',mf2'=matchLits.View.get_arith(C.litsactive_clause)active_poswith|Some(ALF.Right(_,m1',mf2'))->m1',mf2'|_->assertfalseinletmf2'=MF.apply_substSubst.Renaming.nonesubst(mf2',1)inletm1'=M.apply_substSubst.Renaming.nonesubst(m1',1)inletnew_plit=ALF.replaceplit(M.differencem1'(MF.restmf2'))in(* transitive closure *)lettrace=active_clause::tracein_ineq_find_sufficient~ord~tracecnew_plitk)|Some_->()(* cannot make a sufficient literal *)))|_->()(* is a literal redundant w.r.t the current set of unit clauses *)let_ineq_is_redundant_by_unitclit=matchlitwith|_whenLit.is_triviallit||Lit.is_absurdlit->None(* something more efficient will take care of it *)|Lit.Int(AL.Binary(AL.Lesseq,_m1,_m2)asalit)->letord=Ctx.ord()inlettraces=_ineq_find_sufficient~ord~trace:[]calit|>Iter.head(* one is enough *)inbeginmatchtraceswith|Some(trace,_lit')->assert(AL.is_trivial_lit');lettrace=CCList.uniq~eq:C.equaltraceinSometrace|None->Noneend|_->Noneletis_redundant_by_ineqc=Util.enter_profprof_arith_trivial_ineq;letres=CCArray.exists(funlit->match_ineq_is_redundant_by_unitclitwith|None->false|Sometrace->Util.debugf~section3"@[<2>clause @[%a@]@ trivial by inequations @[%a@]@]"(funk->kC.ppc(CCFormat.listC.pp)trace);Util.incr_statstat_arith_trivial_ineq_steps;true)(C.litsc)inUtil.exit_profprof_arith_trivial_ineq;res(* allow traces of depth at most 3 *)letmax_ineq_demod_steps=3(* rewrite a literal [l] into a smaller literal [l'], such that [l] and
the current set of unit clauses imply [l']; then compute the
transitive closure of this relation. If we obtain an absurd
literal, then [l] is absurd (we keep a trace of literals used).
We use continuations to deal with the multiple choices.
Each step looks like: from [l == (t <= u) && l' == (l <= t)]
we deduce [l <= u]. If at some point we deduce [⊥], we win. *)letrecineq_find_necessary_~ord~traceclitk=matchlitwith|_whenAL.is_absurdlit->k(trace,lit)|_whenList.lengthtrace>=max_ineq_demod_steps->()(* need another step, but it would exceed the limit *)|AL.Binary_whenIter.existsT.is_var(AL.Seq.termslit)->()(* too costly (will match too many things) *)|AL.Binary(AL.Lesseq,_,_)->Util.incr_statstat_arith_demod_ineq;Util.debugf~section5"(@[try_ineq_find_necessary@ :lit `%a`@ :trace (@[%a@])@])"(funk->kAL.pplit(Util.pp_listC.pp)trace);AL.fold_terms~vars:false~which:`Max~ord~subterms:falselit|>Iter.iter(fun(t,pos)->letplit=ALF.get_exnlitposinletis_left=matchposwith|Position.Left_->true|Position.Right_->false|_->assertfalsein(* try to eliminate [t] in passive lit [plit]*)PS.TermIndex.retrieve_generalizations(!_idx_unit_ineq,1)(t,0)|>Iter.iter(fun(_t',with_pos,subst)->letactive_clause=with_pos.C.WithPos.clauseinletactive_pos=with_pos.C.WithPos.posinmatchLits.View.get_arith(C.litsactive_clause)active_poswith|None->assertfalse|Some(ALF.Left(AL.Lesseq,_,_)asalit')whennotis_left->letalit'=ALF.apply_substSubst.Renaming.nonesubst(alit',1)inifC.trail_subsumesactive_clausec&&ALF.is_strictly_max~ordalit'then((* scale *)letplit,_alit'=ALF.scaleplitalit'inletmf1',m2'=matchLits.View.get_arith(C.litsactive_clause)active_poswith|Some(ALF.Left(_,mf1',m2'))->mf1',m2'|_->assertfalseinletmf1'=MF.apply_substSubst.Renaming.nonesubst(mf1',1)inletm2'=M.apply_substSubst.Renaming.nonesubst(m2',1)in(* from m1 ≤ t+mf2 and t+mf1' ≤ m2', we deduce
m1 + mf1' ≤ mf2 + m2'. If this literal is absurd
then so is [m1 ≤ t+mf2].
We replace [t] with [m2'-mf1'] *)letnew_plit=ALF.replaceplit(M.differencem2'(MF.restmf1'))in(* transitive closure *)lettrace=active_clause::traceinineq_find_necessary_~ord~tracecnew_plitk)|Some(ALF.Right(AL.Lesseq,_,_)asalit')whenis_left->(* symmetric case *)letalit'=ALF.apply_substSubst.Renaming.nonesubst(alit',1)inifC.trail_subsumesactive_clausec&&ALF.is_strictly_max~ordalit'then((* scale *)letplit,_alit'=ALF.scaleplitalit'inletm1',mf2'=matchLits.View.get_arith(C.litsactive_clause)active_poswith|Some(ALF.Right(_,m1',mf2'))->m1',mf2'|_->assertfalseinletmf2'=MF.apply_substSubst.Renaming.nonesubst(mf2',1)inletm1'=M.apply_substSubst.Renaming.nonesubst(m1',1)inletnew_plit=ALF.replaceplit(M.differencem1'(MF.restmf2'))in(* transitive closure *)lettrace=active_clause::traceinineq_find_necessary_~ord~tracecnew_plitk)|Some_->()(* cannot make a sufficient literal *)))|_->()(* is a literal absurd w.r.t the current set of unit clauses *)let_ineq_is_absurd_by_unitclit=matchlitwith|_whenLit.is_triviallit||Lit.is_absurdlit->None(* something more efficient will take care of it *)|Lit.Int(AL.Binary(AL.Lesseq,_m1,_m2)asalit)->letord=Ctx.ord()inlettraces=ineq_find_necessary_~ord~trace:[]calit|>Iter.head(* one is enough *)inbeginmatchtraceswith|Some(trace,_lit')->assert(AL.is_absurd_lit');lettrace=CCList.uniq~eq:C.equaltraceinSometrace|None->Noneend|_->None(* demodulate using inequalities *)letdemod_ineqc:C.tSimplM.t=Util.enter_profprof_arith_demod_ineq;letres=CCArray.findi(funilit->match_ineq_is_absurd_by_unitclitwith|None->None|Sometrace->Util.debugf~section3"@[<2>clause @[%a@]@ rewritten by inequations @[%a@]@]"(funk->kC.ppc(CCFormat.listC.pp)trace);Util.incr_statstat_arith_demod_ineq_steps;Some(i,trace))(C.litsc)inletres=matchreswith|None->SimplM.return_samec|Some(i,cs)->letlits=CCArray.except_idx(C.litsc)iinletproof=Proof.Step.simp~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"int.demod_ineq")(C.proof_parentc::List.mapC.proof_parentcs)inletc'=C.createlitsproof~penalty:(C.penaltyc)~trail:(C.trailc)inSimplM.return_newc'inUtil.exit_profprof_arith_demod_ineq;res(** {3 Divisibility} *)letcanc_div_chainingc=letord=Ctx.ord()inUtil.enter_profprof_arith_div_chaining;leteligible=C.Eligible.(maxc**filterLit.is_arith_divides)inletsc1=0andsc2=1in(* do the inference (if ordering conditions are ok) *)let_do_chaining~signnpowerc1lit1pos1c2lit2pos2usacc=letrenaming=Subst.Renaming.create()inletsubst=US.substusinletidx1=Lits.Pos.idxpos1andidx2=Lits.Pos.idxpos2inletlit1'=ALF.apply_substrenamingsubst(lit1,sc1)inletlit2'=ALF.apply_substrenamingsubst(lit2,sc2)inletlit1',lit2'=ALF.scalelit1'lit2'inletmf1'=ALF.focused_monomelit1'andmf2'=ALF.focused_monomelit2'in(* now we have two literals with the same power and coeff *)letgcd=Z.gcd(MF.coeffmf1')(MF.coeffmf2')in(* check that we didn't "overflow", and that ordering conditions
are good *)Util.debugf~section5"@[<2>div. chaining@ with @[%a@]@ between @[%a@] (at %a)@ and@ @[%a@] (at %a)@]"(funk->kSubst.ppsubstC.ppc1Position.pppos1C.ppc2Position.pppos2);ifZ.ltgcdZ.(pownpower)&&C.is_maxlit(c1,sc1)subst~idx:idx1&&C.is_maxlit(c2,sc2)subst~idx:idx2&&ALF.is_max~ordlit1'&&ALF.is_max~ordlit2'then(letnew_lit=Lit.mk_divides~signn~power(M.difference(MF.restmf1')(MF.restmf2'))inletlits1=CCArray.except_idx(C.litsc1)idx1andlits2=CCArray.except_idx(C.litsc2)idx2inletlits1=Lit.apply_subst_listrenamingsubst(lits1,sc1)andlits2=Lit.apply_subst_listrenamingsubst(lits2,sc2)inletc_guard=Literal.of_unif_substrenamingusinletall_lits=new_lit::c_guard@lits1@lits2inletproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"div_chaining")[C.proof_parent_substrenaming(c1,sc1)subst;C.proof_parent_substrenaming(c2,sc2)subst]inlettrail=C.trail_l[c1;c2]in(* penalize chaining into variables *)letpenalty=C.penaltyc1+C.penaltyc2+(ifMF.termmf1'|>T.is_varthen10else0)+(ifMF.termmf2'|>T.is_varthen10else0)inletnew_c=C.create~trail~penaltyall_litsproofinUtil.debugf~section5"@[<4>... gives@ @[%a@]@]"(funk->kC.ppnew_c);Util.incr_statstat_arith_div_chaining;new_c::acc)else(Util.debug~section5"... has bad ordering conditions";acc)inletres=Lits.fold_arith_terms~eligible~which:`Max~ord(C.litsc)|>Iter.fold(funacc(t,lit1,pos1)->matchlit1with|ALF.Divd1whenAL.Util.is_primed1.AL.num->(* inferences only possible when lit1 is a power-of-prime *)letn=d1.AL.numinPS.TermIndex.retrieve_unifiables(!_idx_div,sc2)(t,sc1)|>Iter.fold(funacc(_t',with_pos,subst)->(* [subst t = subst t'], see whether they belong to the same group *)letc2=with_pos.C.WithPos.clauseinletpos2=with_pos.C.WithPos.posinletlit2=Lits.View.get_arith_exn(C.litsc2)pos2inmatchlit2with|ALF.Divd2when(d1.AL.sign||d2.AL.sign)&&Z.equalnd2.AL.num->(* inference seems possible (at least one lit is positive).
start with scaling the literals to the same power *)letsign=d1.AL.sign&&d2.AL.signinletpower=maxd1.AL.powerd2.AL.powerinletlit1=ALF.scale_powerlit1powerandlit2=ALF.scale_powerlit2powerinletmf1=ALF.focused_monomelit1andmf2=ALF.focused_monomelit2in(* unify mf1 and mf2 as possible *)MF.unify_ff~subst(mf1,sc1)(mf2,sc2)|>Iter.fold(funacc(_,_,subst)->_do_chaining~signnpowerclit1pos1c2lit2pos2substacc)acc|_->acc)acc|_->acc)[]inUtil.exit_profprof_arith_div_chaining;resexceptionReplaceLitByLitsInSameClauseofint*Lit.tlistexceptionReplaceLitByLitsInManyClausesofint*Lit.tlistletcanc_div_case_switchc=leteligible=C.Eligible.(maxc**neg**filterLit.is_arith_divides)intryLits.fold_arith~eligible(C.litsc)|>Iter.iter(fun(lit,pos)->matchlitwith|AL.Dividesd->assert(not(d.AL.sign));letn=d.AL.numandpower=d.AL.powerin(* check that [n] is a not-too-big prime *)ifZ.gtnZ.one&&AL.Util.is_primenthenifZ.leqn(Z.of_int!div_case_switch_limit)then(letidx=Lits.Pos.idxposin(* build the list of alternatives *)letlits=ref[]infore=0topower-1dofori=1toZ.to_intn-1do(* new lit: n^{e+1} | m + i·n^e *)letm'=M.add_constd.AL.monomeZ.((n**e)*of_inti)inletnew_lit=Lit.mk_dividesn~power:(e+1)m'inlits:=new_lit::!litsdonedone;raise(ReplaceLitByLitsInSameClause(idx,!lits)))elseCtx.lost_completeness()else()|_->assertfalse);[]withReplaceLitByLitsInSameClause(i,lits)->(* replace lit number [i] with [lits] *)letlits'=CCArray.except_idx(C.litsc)iinletall_lits=List.rev_appendlitslits'inletproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"div_case_switch")[C.proof_parentc]inletnew_c=C.create~trail:(C.trailc)~penalty:(C.penaltyc)all_litsproofinUtil.debugf~section5"@[<2>div_case_switch@ of @[%a@]@ into @[%a@]@]"(funk->kC.ppcC.ppnew_c);[new_c]let_pp_zoutz=Z.pp_printoutzlet_pp_divoutd=Format.fprintfout"%a^%d"_pp_zd.AL.Util.primed.AL.Util.powerletcanc_div_prime_decompositionc=leteligible=C.Eligible.(maxc**filterLit.is_arith_divides)intryLits.fold_arith~eligible(C.litsc)|>Iter.iter(fun(lit,pos)->matchlitwith|AL.Dividesdwhend.AL.sign->(* positive "divides" predicate *)letn=d.AL.numin(* check that [n] is a composite number *)ifZ.gtnZ.one&¬(AL.Util.is_primen)then(letn'=Z.pownd.AL.powerinletidx=Lits.Pos.idxposinletdivisors=AL.Util.prime_decompositionn'inUtil.debugf~section5"@[<2>composite num:@ @[%a = %a@]@]"(funk->k_pp_zn'(Util.pp_list_pp_div)divisors);letlits=List.map(fundiv->Lit.mk_divides~sign:truediv.AL.Util.prime~power:div.AL.Util.powerd.AL.monome)divisorsinraise(ReplaceLitByLitsInManyClauses(idx,lits)))|AL.Dividesd->(* negative "divides" predicate *)letn=d.AL.numin(* check that [n] is a composite number *)ifZ.gtnZ.one&¬(AL.Util.is_primen)then(letn'=Z.pownd.AL.powerinletidx=Lits.Pos.idxposinletdivisors=AL.Util.prime_decompositionn'inUtil.debugf~section5"@[<2>composite num:@ @[%a = %a@]@]"(funk->k_pp_zn'(Util.pp_list_pp_div)divisors);assert(List.lengthdivisors>=2);letlits=List.map(fundiv->Lit.mk_divides~sign:falsediv.AL.Util.prime~power:div.AL.Util.powerd.AL.monome)divisorsinraise(ReplaceLitByLitsInSameClause(idx,lits)))|_->assertfalse);Nonewith|ReplaceLitByLitsInSameClause(i,lits)->(* replace lit number [i] with [lits] *)letlits'=CCArray.except_idx(C.litsc)iinletall_lits=List.rev_appendlitslits'inletproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"div_prime_decomposition")[C.proof_parentc]inletnew_c=C.create~trail:(C.trailc)~penalty:(C.penaltyc)all_litsproofinUtil.debugf~section5"@[<2>prime_decomposition- of@ @[%a@]@ into @[%a@]@]"(funk->kC.ppcC.ppnew_c);Some[new_c]|ReplaceLitByLitsInManyClauses(i,lits)->letclauses=List.map(funlit->letall_lits=Array.copy(C.litsc)inall_lits.(i)<-lit;letproof=Proof.Step.inference~tags:[Proof.Tag.T_lia][C.proof_parentc]~rule:(Proof.Rule.mk"div_prime_decomposition")inletnew_c=C.create_a~trail:(C.trailc)~penalty:(C.penaltyc)all_litsproofinnew_c)litsinUtil.debugf~section5"@[<2>prime_decomposition+@ of @[%a@]@ into set {@[%a@]}@]"(funk->kC.ppc(Util.pp_listC.pp)clauses);Someclausesletcanc_divisibilityc=Util.enter_profprof_arith_divisibility;(* inference on 1/ positive eq 2/ positive divisibility *)leteligible=C.Eligible.(maxc**(filterLit.is_arith_eq++(pos**filterLit.is_arith_divides)))inletord=Ctx.ord()inletres=Lits.fold_arith_terms~eligible~which:`Max~ord(C.litsc)|>Iter.fold(funacc(_t,lit,pos)->letmf=ALF.focused_monomelitinletidx=Lits.Pos.idxposinMF.unify_self(mf,0)|>Iter.fold(funacc(_,us)->letrenaming=Subst.Renaming.create()inletsubst=US.substusinletlit'=ALF.apply_substrenamingsubst(lit,0)inletmf'=ALF.focused_monomelit'in(* does the maximal term have a coeff bigger-than-one? *)letn=MF.coeffmf'inifZ.gtnZ.one&&C.is_maxlit(c,0)subst~idx&&ALF.is_max~ordlit'&&(* in case we have a divisibility, only infer if the coefficient
of [t] divides [d^k]. In particular it means [n] is a
power-of-prime *)beginmatchlit'with|ALF.Divd->Z.sign(Z.rem(Z.powd.AL.numd.AL.power)n)=0|_->trueendthen((* do the inference *)Util.debugf~section5"@[<2>divisibility@ on @[%a@]@ at @[%a@]@ with @[%a@]...@]"(funk->kC.ppcPosition.ppposSubst.ppsubst);letnew_lit=matchlit'with|ALF.Left(AL.Equal,mf,m)|ALF.Right(AL.Equal,m,mf)->(* remove the max term from [mf], and inject into the Z/nZ group *)Lit.mk_dividesn~power:1(M.differencem(MF.restmf))|ALF.Divd->assertd.AL.sign;letn',power'=matchAL.Util.prime_decompositionnwith|[{AL.Util.prime=n';AL.Util.power=p}]->assert(Z.equaln'd.AL.num);assert(p<=d.AL.power);n',p|_->assertfalseinLit.mk_dividesn'~power:power'(MF.restd.AL.monome)|_->assertfalseinletlits'=CCArray.except_idx(C.litsc)idxinletlits'=Lit.apply_subst_listrenamingsubst(lits',0)inletc_guard=Literal.of_unif_substrenamingusinletall_lits=new_lit::c_guard@lits'inletproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"divisibility")[C.proof_parent_substrenaming(c,0)subst]inletnew_c=C.create~trail:(C.trailc)~penalty:(C.penaltyc)all_litsproofinUtil.debugf~section5"@[<4>... gives@ @[%a@]@]"(funk->kC.ppnew_c);Util.incr_statstat_arith_divisibility;new_c::acc)elseacc)acc)[]inUtil.exit_profprof_arith_divisibility;res(* regular literal ----> arith literal, sometimes *)letcanc_lit_of_litlit=matchlitwith|Lit.Equation(l,r,sign)whenType.equalType.int(T.tyl)->beginmatchT.viewl,T.viewrwith|T.AppBuiltin(Builtin.Remainder_e,[l';r']),opp|opp,T.AppBuiltin(Builtin.Remainder_e,[l';r'])->beginmatchMonome.Int.of_terml',T.viewr',oppwith|Somem,T.AppBuiltin(Builtin.Intn,[]),T.AppBuiltin(Builtin.Intopp',[])whenZ.signopp'>=0&&Z.compareopp'n<0->(* remainder(l1, n) = opp ----> n | l1-opp,
assuming 0<=opp<n *)letm=M.add_constmZ.(rem(~-opp')n)inletlit=Lit.mk_divides~signn~power:1minSome(lit,[],[Proof.Tag.T_lia])|Some_,T.AppBuiltin(Builtin.Intn,[]),T.AppBuiltin(Builtin.Intopp',[])whenZ.signopp'<0||Z.compareopp'n>=0->(* remainder(l1, n) = opp --> false
assuming opp ∉ [0.. n-1] *)letlit=ifsignthenLit.mk_absurdelseLit.mk_tautoinSome(lit,[],[Proof.Tag.T_lia])|_->Noneend|_->beginmatchMonome.Int.of_terml,Monome.Int.of_termrwith|Somem1,Somem2->ifsignthenSome(Lit.mk_arith_eqm1m2,[],[Proof.Tag.T_lia])elseSome(Lit.mk_arith_neqm1m2,[],[Proof.Tag.T_lia])|_,None|None,_->Noneendend|_->None(** {3 Others} *)let_has_arithc=CCArray.existsLit.is_arith(C.litsc)moduleSimp=Simplex.MakeHelp(T)(* tautology check: take the linear system that is the negation
of all a≠b and a≤b, and check its (rational) satisfiability. If
it's unsat in Q, it's unsat in Z, and its negation (a subset of c)
is tautological *)let_is_tautologyc=Util.enter_profprof_arith_semantic_tautology;(* convert a monome into a rational monome + Q constant *)letto_ratm=letconst=Q.of_bigint(M.constm)inList.map(fun(c,t)->Q.of_bigintc,t)(M.coeffsm),constin(* create a list of constraints for some arith lits *)letconstraints=Lits.fold_arith~eligible:C.Eligible.arith(C.litsc)|>Iter.fold(funacc(lit,_)->(* negate the literal and make a constraint out of it *)matchlitwith|AL.Binary(AL.Lesseq,m1,m2)->(* m1 ≤ m2 ----> m1-m2 > 0 ---> m1-m2 ≥ 1 *)letm,c=to_rat(M.differencem1m2)in(Simp.GreaterEq,m,Q.add(Q.negc)Q.one)::acc|AL.Binary(AL.Different,m1,m2)->(* m1 != m2 -----> (m1-m2) = 0 *)letm,c=to_rat(M.differencem1m2)in(Simp.Eq,m,Q.negc)::acc|_->acc)[]inletsimplex=Simp.add_constraintsSimp.emptyconstraintsinUtil.exit_profprof_arith_semantic_tautology;matchSimp.ksolvesimplexwith|Simp.Unsatisfiable_->true(* negation unsatisfiable *)|Simp.Solution_->false(* cache the result because it's a bit expensive *)letis_tautologyc=ifC.get_flagflag_computed_tautocthenC.get_flagflag_tautocelse((* compute whether [c] is an arith tautology *)letres=_has_arithc&&_is_tautologycinC.set_flagflag_tautocres;C.set_flagflag_computed_tautoctrue;ifresthen(Util.incr_statstat_arith_semantic_tautology_steps;Util.debugf~section4"@[<2>clause@ @[%a@]@ is an arith tautology@]"(funk->kC.ppc););Util.incr_statstat_arith_semantic_tautology;res)(* Simplification: a < b ----> a+1 ≤ b *)letcanc_less_to_lesseq=function|Lit.Int(AL.Binary(AL.Less,m1,m2))->Some(Lit.mk_arith_lesseq(M.succm1)m2,[],[Proof.Tag.T_lia])|_->NoneexceptionVarElimofint*S.t(* X != Y or C -----> C[X/Y] *)letcanc_eq_resolutionc=(* check whether [m] is only one variable with coeff 1 *)letis_unary_varm=matchM.coeffsmwith|[c,t]->beginmatchT.viewtwith|T.VarvwhenZ.(equalcone)&&Z.(equal(M.constm)zero)->Somev|_->Noneend|_->NoneintryLits.fold_arith~eligible:C.Eligible.(filterLit.is_arith_neq)(C.litsc)|>Iter.iter(fun(lit,pos)->matchlitwith|AL.Binary(AL.Different,m1,m2)->beginmatchis_unary_varm1,is_unary_varm2with|Somev1,Somev2->letsubst=S.FO.bindS.empty((v1:Type.tHVar.t:>InnerTerm.tHVar.t),0)(T.varv2,0)inleti=Lits.Pos.idxposinraise(VarElim(i,subst))|_->()end|_->());SimplM.return_samec(* could not simplify *)withVarElim(i,subst)->letlits'=CCArray.except_idx(C.litsc)iinletrenaming=Subst.Renaming.create()inletlits'=Lit.apply_subst_listrenamingsubst(lits',0)inletproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"canc_eq_res")[C.proof_parent_substrenaming(c,0)subst]inletc'=C.create~trail:(C.trailc)~penalty:(C.penaltyc)lits'proofinUtil.debugf~section4"@[<2>arith_eq_res:@ simplify @[%a@]@ into @[%a@]@]"(funk->kC.ppcC.ppc');SimplM.return_newc'exceptionDiffToLesseqofC.tletis_singleton_unshielded_varlits(m:_M.t):bool=Z.sign(M.constm)=0&&beginmatchM.coeffsmwith|[c,t]->Z.equalZ.onec&&T.is_vart&&(not@@Literals.is_shielded(T.as_var_exnt)lits)|_->falseend(* a != b ------> a+1 ≤ b | a ≥ b+1 *)letcanc_diff_to_lesseqc=leteligible=C.Eligible.(filterLit.is_arith_neq**maxc)intryLits.fold_lits~eligible(C.litsc)|>Iter.iter(fun(lit,i)->matchlitwith|Lit.Int(AL.Binary(AL.Different,m1,m2))whennot(is_singleton_unshielded_var(C.litsc)m1)&¬(is_singleton_unshielded_var(C.litsc)m2)->(* translate [m1 ≠ m2] into [m1 < m2 ∨ m1 > m2],
do not do it on a variable that is going to be eliminated. *)assert(eligibleilit);(*Format.printf
"@[<2>lit @[%a [%d]@]@ in @[%a@]@ :is-max %B@ :max_lits %a@]@."
Lit.pp lit i C.pp c (Lits.is_max ~ord (C.lits c) i)
CCBV.print (Lits.maxlits ~ord @@ C.lits c);*)(* FIXME: find why this sometimes fails
assert (Lits.is_max ~ord (C.lits c) i); *)letlits=CCArray.except_idx(C.litsc)iinletnew_lits=[Lit.mk_arith_lesseq(M.succm1)m2;Lit.mk_arith_lesseq(M.succm2)m1]inletproof=Proof.Step.inference[C.proof_parentc]~tags:[Proof.Tag.T_lia]~rule:(Proof.Rule.mk"arith_diff_to_lesseq")inletc'=C.create~trail:(C.trailc)~penalty:(C.penaltyc)(new_lits@lits)proofinUtil.debugf~section5"@[<2>diff2less:@ @[%a@]@ into @[%a@]@]"(funk->kC.ppcC.ppc');raise(DiffToLesseqc')|Lit.Int(AL.Binary(AL.Different,_,_))->()|_->assertfalse);SimplM.return_samecwithDiffToLesseqc->SimplM.return_newc(* inference rule corresponding to {!canc_diff_to_lesseq} *)letcanc_diff_imply_lesseqc=letc,st=canc_diff_to_lesseqcinmatchstwith|`New->[c]|`Same->[](** {6 Variable Elimination Procedure} *)letnaked_varslits=Literals.unshielded_varslits~filter:(funvar->Type.equal(HVar.tyvar)Type.int)(** Description of a clause, focused around the elimination
of some variable x *)moduleNakedVarElim=structtypet={rest:Literal.tlist;(* x doesn't occur in rest *)x:Type.tHVar.t;(* variable to eliminate *)a_lit:ALF.tlist;(* c.x + m1 = m2 *)b_lit:ALF.tlist;(* c.x + m1 != m2 *)c_lit:ALF.tlist;(* n | m_c.x + m *)d_lit:ALF.tlist;(* n not| m_c.x + m *)e_lit:ALF.tlist;(* c.x + m1 ≤ m2 *)f_lit:ALF.tlist;(* m1 ≤ c.x + m2 *)lcm:Z.t;(* scaling coefficient (divisibility guard) *)delta:Z.t;(* lcm of all divisibility constraints *)}let_emptyx={rest=[];x;a_lit=[];b_lit=[];c_lit=[];d_lit=[];e_lit=[];f_lit=[];lcm=Z.one;delta=Z.one;}let_litsck=List.iterkc.a_lit;List.iterkc.b_lit;List.iterkc.c_lit;List.iterkc.d_lit;List.iterkc.e_lit;List.iterkc.f_lit;()letmapfc={cwitha_lit=List.mapfc.a_lit;b_lit=List.mapfc.b_lit;c_lit=List.mapfc.c_lit;d_lit=List.mapfc.d_lit;e_lit=List.mapfc.e_lit;f_lit=List.mapfc.f_lit;}(* map literals to 'a option, and return a list of 'a *)letmap_litsfc=List.concat[List.mapfc.a_lit;List.mapfc.b_lit;List.mapfc.c_lit;List.mapfc.d_lit;List.mapfc.e_lit;List.mapfc.f_lit](* reduce all occurrences of [x] to the same coefficient (their LCM).
then, pretend we replace LCM.x with x, so all the coefficients
become 1 (but the monomes keep their multiplicative factors!) *)letscalec=letlcm,delta=_litsc|>Iter.fold(fun(lcm,delta)lit->letlcm=Z.lcmlcm(ALF.focused_monomelit|>MF.coeff)inletdelta=matchlitwith|ALF.Divd->Z.lcmdelta(Z.powd.AL.numd.AL.power)|_->deltainlcm,Z.lcmlcmdelta)(Z.one,Z.one)inassert(Z.geqdeltalcm);ifZ.equallcmZ.onethendelta,{cwithdelta;}elseletc'=map(funlit->letn=Z.divexactlcm(ALF.focused_monomelit|>MF.coeff)in(* scale monome by n *)ALF.productlitn)cinletc'={c'withlcm;delta;}indelta,c'(* make from clause *)letof_litslitsx=Array.fold_left(funacclit->matchlitwith|Lit.IntowhenLit.var_occursxlit->(* one of the literals [x] occurs in! classify it, but
remember that we need to {b negate} it first. *)beginmatchAL.Focus.focus_term(AL.negateo)(T.varx)with|None->assertfalse|Some(ALF.Left(AL.Equal,_,_)aslit)|Some(ALF.Right(AL.Equal,_,_)aslit)->{accwitha_lit=lit::acc.a_lit;}|Some(ALF.Left(AL.Different,_,_)aslit)|Some(ALF.Right(AL.Different,_,_)aslit)->{accwithb_lit=lit::acc.b_lit;}|Some(ALF.Divdaslit)whend.AL.sign->{accwithc_lit=lit::acc.c_lit;}|Some(ALF.Divdaslit)->assert(not(d.AL.sign));{accwithd_lit=lit::acc.d_lit;}|Some(ALF.Left(AL.Lesseq,_,_)aslit)->{accwithe_lit=lit::acc.e_lit;}|Some(ALF.Left(AL.Less,mf1,m2))->(* mf1 < m2 ------> mf1 ≤ m2-1 *)letlit=ALF.Left(AL.Lesseq,mf1,M.predm2)in{accwithe_lit=lit::acc.e_lit;}|Some(ALF.Right(AL.Lesseq,_,_)aslit)->{accwithf_lit=lit::acc.f_lit;}|Some(ALF.Right(AL.Less,m1,mf2))->(* m1 < mf2 -----> m1+1 ≤ mf2 *)letlit=ALF.Right(AL.Lesseq,M.succm1,mf2)in{accwithf_lit=lit::acc.f_lit;}end|_->{accwithrest=lit::acc.rest;})(_emptyx)lits(* higher bounds *)leta_setc=List.concat[List.map(function|ALF.Left(AL.Equal,mf,m)|ALF.Right(AL.Equal,m,mf)->M.difference(M.succm)(MF.restmf)|_->assertfalse)c.a_lit;List.map(function|ALF.Left(AL.Different,mf,m)|ALF.Right(AL.Different,m,mf)->M.differencem(MF.restmf)|_->assertfalse)c.b_lit;List.map(function|ALF.Left(AL.Lesseq,mf,m)->M.difference(M.succm)(MF.restmf)|_->assertfalse)c.e_lit]letb_setc=List.concat[List.map(function|ALF.Left(AL.Equal,mf,m)|ALF.Right(AL.Equal,m,mf)->M.difference(M.predm)(MF.restmf)|_->assertfalse)c.a_lit;List.map(function|ALF.Left(AL.Different,mf,m)|ALF.Right(AL.Different,m,mf)->M.differencem(MF.restmf)|_->assertfalse)c.b_lit;List.map(function|ALF.Right(AL.Lesseq,m,mf)->M.difference(M.predm)(MF.restmf)|_->assertfalse)c.f_lit](* evaluate when the variable is equal to x *)leteval_atcx=Lit.mk_divides~power:1c.lcmx::map_lits(function|ALF.Left(op,mf,m)->Lit.mk_arith_opop(M.sum(MF.restmf)x)m|ALF.Right(op,m,mf)->Lit.mk_arith_opopm(M.sum(MF.restmf)x)|ALF.Divd->Lit.mk_divides~sign:d.AL.signd.AL.num~power:d.AL.power(M.sum(MF.restd.AL.monome)x))c(* evaluate when the variable is equal to x, but as small as needed.
Many literals will become true or false *)leteval_minus_inftycx=Lit.mk_divides~power:1c.lcmx::map_lits(function|ALF.Left(AL.Different,_,_)|ALF.Right(AL.Different,_,_)|ALF.Left(AL.Lesseq,_,_)->Lit.mk_tauto|ALF.Left(AL.Equal,_,_)|ALF.Right(AL.Equal,_,_)|ALF.Right(AL.Lesseq,_,_)->Lit.mk_absurd|ALF.Divd->Lit.mk_divides~sign:d.AL.signd.AL.num~power:d.AL.power(M.sum(MF.restd.AL.monome)x)|ALF.Left(AL.Less,_,_)|ALF.Right(AL.Less,_,_)->assertfalse)c(* evaluate when the variable is equal to x, but as big as needed.
Many literals will become true or false *)leteval_plus_inftycx=Lit.mk_divides~power:1c.lcmx::map_lits(function|ALF.Left(AL.Different,_,_)|ALF.Right(AL.Different,_,_)|ALF.Right(AL.Lesseq,_,_)->Lit.mk_tauto|ALF.Left(AL.Equal,_,_)|ALF.Right(AL.Equal,_,_)|ALF.Left(AL.Lesseq,_,_)->Lit.mk_absurd|ALF.Divd->Lit.mk_divides~sign:d.AL.signd.AL.num~power:d.AL.power(M.sum(MF.restd.AL.monome)x)|ALF.Left(AL.Less,_,_)|ALF.Right(AL.Less,_,_)->assertfalse)cendlet_negate_lits=List.mapLit.negateleteliminate_unshieldedc=letmoduleNVE=NakedVarEliminletnvars=naked_vars(C.litsc)inmatchnvarswith|[]->None|x::_->(* eliminate v *)Util.debugf~section3"@[<2>eliminate naked variable %a@ from @[%a@]@]"(funk->kHVar.ppxC.ppc);(* split C into C' (not containing v) and 6 kinds of literals *)letview=NVE.of_lits(C.litsc)xinletdelta,view=NVE.scaleviewinifnot(Z.fits_intdelta)thenNoneelsebeginletdelta=Z.to_intdeltainleta_set=NVE.a_setviewandb_set=NVE.b_setviewin(* prepare to build clauses *)letacc=ref[]inletadd_clause~by~whichlits=letinfos=UntypedAST.A.([app"var_elim"[quoted(Z.to_stringview.NVE.lcm);quoted(HVar.to_string_tstpx);quoted(CCFormat.to_stringM.ppby);quotedwhich]])in(* TODO: use substitution (for ∞ cases just take sth high enough) *)letrule=Proof.Rule.mkf"var_elim(%a)"T.pp_varxinletproof=Proof.Step.inference~tags:[Proof.Tag.T_lia]~infos~rule[C.proof_parentc]inletnew_c=C.create~trail:(C.trailc)~penalty:(C.penaltyc)litsproofinUtil.debugf~section5"@[<2>elimination of %s×%a@ by %a (which:%s)@ in @[%a@]:@ gives @[%a@]@]"(funk->k(Z.to_stringview.NVE.lcm)HVar.ppxM.ppbywhichC.ppcC.ppnew_c);acc:=new_c::!accin(* choose which form to use *)ifList.lengtha_set>List.lengthb_setthenbegin(* use B *)Util.debug~section5"use the B elimination algorithm";(* first, the -infty part *)fori=1todeltadoletx'=M.Int.const(Z.of_inti)inletlits=view.NVE.rest@_negate_lits(NVE.eval_minus_inftyviewx')inadd_clause~by:x'~which:"-∝"litsdone;(* then the enumeration *)fori=1todeltadoList.iter(funx'->(* evaluate at x'+i *)letx''=M.add_constx'Z.(of_inti)inletlits=view.NVE.rest@_negate_lits(NVE.eval_atviewx'')inadd_clause~by:x''~which:"middle"lits)b_setdone;endelsebegin(* use A *)Util.debug~section5"use the A elimination algorithm";(* first, the +infty part *)fori=1todeltadoletx'=M.Int.constZ.(neg(of_inti))inletlits=view.NVE.rest@_negate_lits(NVE.eval_plus_inftyviewx')inadd_clause~by:x'~which:"+∝"litsdone;(* then the enumeration *)fori=1todeltadoList.iter(funx'->(* evaluate at x'-i *)letx''=M.add_constx'Z.(neg(of_inti))inletlits=view.NVE.rest@_negate_lits(NVE.eval_atviewx'')inadd_clause~by:x''~which:"middle"lits)a_setdone;end;Some!accend(** {2 Setup} *)(* print index into file *)let_print_idxfileidx=CCIO.with_outfile(funoc->letpp_leaf__=()inletout=Format.formatter_of_out_channelocinFormat.fprintfout"@[<2>%a@]@."(PS.TermIndex.to_dotpp_leaf)idx;flushoc)letsetup_dot_printers()=CCOpt.iter(funf->Signal.onceSignals.on_dot_output(fun()->_print_idxf!_idx_unit_eq))!dot_unit_;()letregister()=Util.debug~section2"arith: setup env";(* add inference rules *)Env.add_binary_inf"canc_sup_active"canc_sup_active;Env.add_binary_inf"canc_sup_passive"canc_sup_passive;Env.add_unary_inf"cancellation"cancellation;Env.add_unary_inf"canc_eq_factoring"canc_equality_factoring;Env.add_binary_inf"canc_ineq_chaining"canc_ineq_chaining;Env.add_unary_inf"canc_ineq_factoring"canc_ineq_factoring;Env.add_binary_inf"div_chaining"canc_div_chaining;Env.add_unary_inf"divisibility"canc_divisibility;Env.add_unary_inf"div_case_switch"canc_div_case_switch;Env.add_multi_simpl_rulecanc_div_prime_decomposition;Env.add_multi_simpl_ruleeliminate_unshielded;Env.add_lit_rule"canc_lit_of_lit"canc_lit_of_lit;Env.add_lit_rule"less_to_lesseq"canc_less_to_lesseq;(* transformation ≠ to ≤ *)beginmatch!diff_to_lesseq_with|`Simplify->Env.add_unary_simplifycanc_diff_to_lesseq|`Inf->Env.add_unary_inf"canc_diff_imply_lesseq"canc_diff_imply_lesseqend;Env.add_basic_simplifycanc_eq_resolution;Env.add_unary_simplifycanc_demodulation;Env.add_backward_simplifycanc_backward_demodulation;Env.add_is_trivialis_tautology;if!enable_trivial_ineq_then(Env.add_redundantis_redundant_by_ineq;);if!enable_demod_ineq_then(Env.add_active_simplifydemod_ineq;);Env.add_multi_simpl_ruleeliminate_unshielded;(* completeness? I don't think so *)Ctx.lost_completeness();(* enable AC-property of sum *)(* FIXME: currently AC doesn't handle builtins
if !_enable_ac then begin
let sum = ID.Arith.sum in
let ty = Signature.find_exn Signature.TPTP.Arith.full sum in
let module A = Env.flex_get AC.key_ac in
A.add sum ty;
end;
*)setup_dot_printers();()endletk_should_register=Flex_state.create_key()letk_has_arith=Flex_state.create_key()letextension=letenv_actionenv=letmoduleE=(valenv:Env.S)inifE.flex_getk_should_registerthen(letmoduleI=Make(E)inI.register())elseifE.flex_getk_has_ariththen((* arith not enabled, so we cannot solve the problem, do not answer "sat" *)E.Ctx.lost_completeness();)andpost_typing_actionstmtsstate=letmodulePT=TypedSTerminlethas_int=CCVector.to_seqstmts|>Iter.flat_mapStmt.Seq.to_seq|>Iter.flat_map(function|`ID_->Iter.empty|`Tyty->Iter.returnty|`Formt|`Termt->PT.Seq.subtermst|>Iter.filter_mapPT.ty)|>Iter.exists(PT.Ty.equalPT.Ty.int)inletshould_reg=!enable_arith_&&has_intinUtil.debugf~section2"decision to register arith: %B"(funk->kshould_reg);state|>Flex_state.addk_should_registershould_reg|>Flex_state.addk_has_arithhas_intin{Extensions.defaultwithExtensions.name="arith_int";post_typing_actions=[post_typing_action];env_actions=[env_action];}let()=Params.add_opts["--no-int-semantic-tauto",Arg.Clearenable_semantic_tauto_," disable integer arithmetic semantic tautology check";"--int-trivial-ineq",Arg.Setenable_trivial_ineq_," enable integer inequality triviality checking by rewriting";"--no-int-trivial-ineq",Arg.Clearenable_trivial_ineq_," disable integer inequality triviality checking by rewriting";"--int-demod-ineq",Arg.Setenable_demod_ineq_," enable integer demodulation of inequalities";"--no-int-demod-ineq",Arg.Clearenable_demod_ineq_," disable integer demodulation of inequalities";"--int-arith",Arg.Setenable_arith_," enable axiomatic integer arithmetic";"--no-int-arith",Arg.Clearenable_arith_," disable axiomatic integer arithmetic";"--int-ac",Arg.Setenable_ac_," enable AC axioms for integer arithmetic (sum)";"--dot-int-unit",Arg.String(funs->dot_unit_:=Somes)," print arith-int-unit index into file";"--int-inf-diff-to-lesseq",Arg.Unit(fun()->diff_to_lesseq_:=`Inf)," ≠ → ≤ as inference";"--int-simp-diff-to-lesseq",Arg.Unit(fun()->diff_to_lesseq_:=`Simplify)," ≠ → ≤ as simplification"];()