123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170(** {1 Linear Expressions} *)openMc2_coremoduleTM=Term.Maptypenum=Q.ttypet={const:num;terms:numTM.t;}letempty:t={const=Q.zero;terms=TM.empty;}letzero=emptylet[@inline]merge_~f_left~f_right~fbothab:t={const=fbotha.constb.const;terms=TM.merge_safea.termsb.terms~f:(fun_->function|`Leftn->Some(f_leftn)|`Rightn->Some(f_rightn)|`Both(n1,n2)->letn=fbothn1n2inifQ.signn=0thenNoneelseSomen);}let[@inline]addab:t=merge_ab~f_left:(funx->x)~f_right:(funx->x)~fboth:Q.addlet[@inline]diffab:t=merge_ab~f_left:(funx->x)~f_right:Q.neg~fboth:Q.sublet[@inline]equale1e2:bool=Q.equale1.conste2.const&&TM.equalQ.equale1.termse2.termslet[@inline]hash_q(n:Q.t):int=CCHash.combine2(Z.hash@@Q.numn)(Z.hash@@Q.denn)let[@inline]hash(e:t):int=lethash_pair(t,n)=CCHash.combine311(Term.hasht)(hash_qn)inCCHash.combine342(hash_qe.const)(CCHash.iterhash_pair@@TM.to_itere.terms)let[@inline]constn:t={const=n;terms=TM.empty}let[@inline]is_constn:bool=TM.is_emptyn.termslet[@inline]is_zeron:bool=is_constn&&Q.signn.const=0let[@inline]as_singleton(e:t)=ifQ.signe.const=0&¬(TM.is_emptye.terms)then(lett,n=TM.choosee.termsinifTM.is_empty(TM.removete.terms)thenSome(n,t)elseNone)elseNonelet[@inline]mem_termte=TM.memte.termslet[@inline]find_term_exnte=TM.findte.termslet[@inline]get_termte=TM.gette.termslet[@inline]multne:t=ifQ.signn=0thenemptyelse{const=Q.mulne.const;terms=TM.map(Q.muln)e.terms;}let[@inline]nege:t=multQ.minus_oneelet[@inline]diven:t=ifQ.signn=0thenraiseDivision_by_zeroelse{const=Q.dive.constn;terms=TM.map(funx->Q.divxn)e.terms;}letadd_term(n:num)(t:term)(e:t):t=ifQ.signn=0theneelse(tryletn'=TM.findte.termsinletn=Q.addnn'inifQ.signn=0then{ewithterms=TM.removete.terms}else{ewithterms=TM.addtne.terms}withNot_found->{ewithterms=TM.addtne.terms})let[@inline]remove_term(t:term)(e:t):t=ifis_constetheneelse{ewithterms=TM.removete.terms}let[@inline]singletonnt=add_termntemptylet[@inline]singleton1t=singletonQ.onetletsimplify(e:t):t=matchas_singletonewith|None->e|Some(n,t)->letn=ifQ.signn>=0thenQ.oneelseQ.minus_oneinsingletonntletpp_no_parenout(e:t):unit=ifis_constethenQ.pp_printoute.constelse(letpp_constoute=ifQ.signe.const=0then()elseFmt.fprintfout" + %a"Q.pp_printe.constandpp_pairout(t,n)=assert(Q.signn<>0);ifQ.equalnQ.onethenTerm.ppouttelseifQ.equalnQ.minus_onethenFmt.fprintfout"-%a"Term.pptelseFmt.fprintfout"%a·%a"Q.pp_printnTerm.pptinFmt.fprintfout"%a%a"(Util.pp_iter~sep:" + "pp_pair)(TM.to_itere.terms)pp_conste)let[@inline]ppoute=Fmt.fprintfout"(@[%a@])"pp_no_pareneletsingleton_term(e:t):term=ifnot(TM.is_emptye.terms)then(lett,_=TM.choosee.termsinifequale@@singleton1tthentelseError.errorf"LE is supposed to be only one term but is %a"ppe)else(Error.errorf"LE is supposed to be only one term but is %a"ppe)letflatten~(f:term->toption)(e:t):t=TM.fold(funtn_te'->beginmatchftwith|None->add_termn_tte'|Somesub_e->add(multn_tsub_e)e'end)e.terms(conste.const)let[@inline]terms(e:t)=TM.keyse.termslet[@inline]terms_l(e:t)=TM.keyse.terms|>Iter.to_rev_listlet[@inline]as_const(e:t)=ifTM.is_emptye.termsthenSomee.constelseNoneleteval_full_~f(e:t):(num*termlist)option=tryletn,l=TM.fold(funtn_t(sum,l)->matchftwith|None->raiseExit|Someq->Q.add(Q.muln_tq)sum,t::l)e.terms(e.const,[])inSome(n,l)withExit->Nonelet[@inline]eval~fe:_option=ifis_constethenSome(e.const,[])elseeval_full_~femoduleInfix=structlet(+..)=addlet(-..)=difflet(*..)=multend