123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698(*---------------------------------------------------------------------------
Copyright (c) 2009 The react programmers. All rights reserved.
Distributed under the ISC license, see terms at the end of the file.
---------------------------------------------------------------------------*)leterr_max_rank="maximal rank exceeded"leterr_sig_undef="signal value undefined yet"leterr_fix="trying to fix a delayed value"leterr_retain_never="E.never cannot retain a closure"leterr_retain_cst_sig="constant signals cannot retain a closure"leterr_step_executed="step already executed"leterr_event_scheduled="event already scheduled on a step"leterr_signal_scheduled="signal already scheduled on a step"moduleWa=structtype'at={mutablearr:'aWeak.t;mutablelen:int}(* The type for resizeable weak arrays.
For now the arrays only grow. We could try to compact and
downsize the array in scan_add if a threshold of empty slots is
exceeded. *)letcreatesize={arr=Weak.createsize;len=0}letlengtha=a.lenletis_emptya=tryfori=0toa.len-1doifWeak.checka.arrithenraiseExit;done;truewithExit->falseletcleara=a.arr<-Weak.create0;a.len<-0letgetai=Weak.geta.arriletsetai=Weak.seta.arriletswapaii'=letv=Weak.geta.arri'inWeak.blita.arria.arri'1;(* blit prevents i from becoming live. *)Weak.seta.arrivletgrowa=letarr'=Weak.create(2*(a.len+1))inWeak.blita.arr0arr'0a.len;a.arr<-arr'letaddav=(* adds v at the end of a. *)ifa.len=Weak.lengtha.arrthengrowa;Weak.seta.arra.len(Somev);a.len<-a.len+1letscan_addav=(* adds v to a, tries to find an empty slot, O(a.len). *)tryfori=0toa.len-1domatchWeak.geta.arriwith|None->Weak.seta.arri(Somev);raiseExit|Some_->()done;addavwithExit->()letrem_lasta=letl=a.len-1in(a.len<-l;Weak.seta.arrlNone)letremav=(* removes v from a, uses physical equality, O(a.len). *)tryfori=0toa.len-1domatchWeak.geta.arriwith|Somev'whenv==v'->Weak.seta.arriNone;raiseExit|_->()donewithExit->()letiterfa=fori=0toa.len-1domatchWeak.geta.arriwithSomev->fv|None->()doneletfoldfacca=letacc=refaccinfori=0toa.len-1domatchWeak.geta.arriwithSomev->acc:=f!accv|None->()done;!accendtypenode={mutablerank:int;(* its rank (height) in the dataflow graph. *)mutablestamp:step;(* last step in which it was scheduled. *)mutableretain:unit->unit;(* retained by the node, NEVER invoked. *)mutableproducers:unit->nodelist;(* nodes on which it depends. *)mutableupdate:step->unit;(* update closure. *)deps:nodeWa.t}(* weak references to dependent nodes. *)(* The type for nodes.
Each event and (non-constant) signal has an associated node. The
fields producers and update keep, in their closure environment,
references to mutables (see later) on which the node depends.
Defining their contents via a let rec allows the environment to be
shared by the two closures.
There are special nodes to represent infinitesimally delayed nodes
(needed for recursive definitions). These nodes all have a rank of
Node.delayed_rank and depend only on the node they delay. Since
they have the highest rank possible they are updated only at the
end of the step and treated specially at that point (see
Step.execute). *)andstep={mutableover:bool;(* true when the step is over. *)mutableheap:heap;(* min-heap of nodes sorted by rank. *)mutableeops:(unit->unit)list;(* end of step operations. *)mutablecops:(unit->unit)list}(* cleanup step operations. *)(* The type for update steps.
Note for historical reasons we use the variable names [c] and [c']
in the code for representing update steps.
There are four successive phases in the execution of a step c (see
Step.execute).
1. Nodes are updated in topological order until c.heap is empty or
we reach a delayed node.
2. End of step operations are executed. This may add new
dependencies (see S.diff and S.changes) and clear the occurence
of delayed events from a previous step (but used in this
step).
3. If there are delayed nodes in c.heap, we create a new step
c'. Each delayed node is updated and its dependents are put in
c'.heap. For delayed events, an end of step operation is added
in c' to clear the occurence at step 2 of c'. Delayed nodes are
updated in any order as a delayed node updating in a step
cannot depend on a delayed node updating in the same step.
4. Cleanup operations are executed. This clears the event occurences of
non-delayed event that occured in c.
After this, if a step c' was created in 3. the step gets executed. *)andheap=nodeWa.t(* The type for heaps.
Weak min-heaps of nodes sorted according to their rank. Classic
imperative implementation with a twist to accomodate the fact
that nodes may disappear.
The heap property we maintain is that for any node its descendents
(vs. children) are either of no smaller rank or they are None. None
nodes need to be treated specially in percolate up and down. The
reason is that it blocks information about the rank of their
descendents. In percolate down the solution is to systematically
swap with None children. So do we in percolate up, however, in
that case we may violate the property if we swap with a None node
and stop right after (either because we got the root or we found a
parent of smaller rank), the property can however be reestablished
by percolating down from that point. *)type'aemut={ev:'aoptionref;(* during steps, holds a potential occurence. *)enode:node;}(* associated node. *)type'aevent=Never|Emutof'aemut(* The type for events.
An event is either the never occuring event Never or a mutable
Emut. A mutable m has some value in m.v iff a step is being
executed and m has an occurence in the step. m's dependents are
scheduled for update iff m has a value in m.v.
Mutables that occur in a step are set back to None when the step
terminates with an cleanup step operation (see eupdate and
Step.execute). To avoid a weak reference on m in the cleanup
operation, the field m.v is a field on a reference instead of a
mutable field.
A new node n can be made dependent on a an event mutable m during a
step. But when n is added to m's dependents, m may already have
updated and scheduled its dependents. In that case n also need to
be scheduled (see E.add_dep). If m only occurs later in the step,
the n will be scheduled as usual with the others. *)type'asmut={mutablesv:'aoption;(* signal value (None only temporary). *)eq:'a->'a->bool;(* to detect signal value changes. *)snode:node}(* associated node. *)type'asignal=Constof'a|Smutof'asmut(* The type for signals.
A signal is either a constant signal Const or a mutable Smut. A
mutable m has a value in m.v iff m.v initialized. m's dependents
are scheduled for update iff m is initialized and m.v changed
according to m.eq in the step.
Signal initialization occurs as follows. If we have an init. value
we set the signal's value to this value and then :
1. If the creation occurs outside a step, the signal's update
function is invoked with Step.nil. This may overwrite the
init. value, but no dependent will see this change as there
cannot be any at that time.
2. If the creation occurs inside a step, the signal is scheduled
for update. Here again this may overwrite the init. value. If
the new value is equal to the init. value this will not schedule
the signals' dependents. However this is not a problem since
dependents are either new signals and will be scheduled via the
init. process or a new dependency added by S.switch in which
case this dependent is also be scheduled.
Note that in both cases if we had no init. value, the call to the
update function must unconditionaly write a concrete value for the
signal.
To find out whether the creation occurs in a step we walk back the
signal's producers recursively looking for a node stamp with an
unfinished step (see Step.find_unfinished). This is not in favor
of static signal creation but this is the price we have to pay for
not having global data structures.
A new node n can be made dependent on a signal mutable m during a
step. In contrast to events (see above) nothing special has to be
done. Here's the rationale :
1. If n is the node of a new event then either the event cannot
happen in the same step and thus the depency addition occurs at
the end of the step (S.diff, S.changes) or the event cares only
about having an up to date value if some other event occurs
(S.sample, E.on) in the same step and the rank of n ensures
this.
2. If n is the node of a new signal then n cares only about having
m's up to date values whenever n will initialize and the rank of
n ensures this. *)moduleH=structletsize=Wa.lengthletelsh=Wa.fold(funacce->e::acc)[]h(* no particular order. *)letcompare_downhii'=matchWa.gethi,Wa.gethi'with|Somen,Somen'->comparen.rankn'.rank|Some_,None->1(* None is smaller than anything. *)|None,Some_->-1(* None is smaller than anything. *)|None,None->0letrecdownhi=letlast=sizeh-1inletstart=2*iinletl=start+1in(* left child index. *)letr=start+2in(* right child index. *)ifl>lastthen()(* no child, stop *)elseletchild=(* index of smallest child. *)ifr>lastthenlelse(ifcompare_downhlr<0thenlelser)inifcompare_downhichild>0then(Wa.swaphichild;downhchild)letuphi=letrecauxhilast_none=ifi=0then(iflast_nonethendownh0)elseletp=(i-1)/2in(* parent index. *)matchWa.gethi,Wa.gethpwith|Somen,Somen'->ifcomparen.rankn'.rank<0then(Wa.swaphip;auxhpfalse)else(iflast_nonethendownhi)|Some_,None->Wa.swaphip;auxhptrue|None,_->()inauxhifalseletrebuildh=fori=(sizeh-2)/2downto0dodownhidoneletaddhn=Wa.addhn;uph(sizeh-1)letrectakeh=lets=sizehinifs=0thenNoneelseletv=Wa.geth0inbeginifs>1then(Wa.seth0(Wa.geth(s-1));Wa.rem_lasth;downh0)elseWa.rem_lasthend;matchvwithNone->takeh|v->vendletdelayed_rank=max_intmoduleStep=struct(* Update steps. *)typet=stepletnil={over=true;heap=Wa.create0;eops=[];cops=[]}letcreate()=leth=Wa.create11in{over=false;heap=h;eops=[];cops=[]}letaddcn=ifn.stamp==cthen()else(n.stamp<-c;H.addc.heapn)letadd_depscn=Wa.iter(addc)n.depsletadd_eopcop=c.eops<-op::c.eopsletadd_copcop=c.cops<-op::c.copsletallow_reschedulen=n.stamp<-nilletrebuildc=H.rebuildc.heapletrecexecutec=leteopsc=List.iter(funop->op())c.eops;c.eops<-[]inletcopsc=List.iter(funop->op())c.cops;c.cops<-[]inletfinishc=c.over<-true;c.heap<-Wa.create0inletrecupdatec=matchH.takec.heapwith|Somenwhenn.rank<>delayed_rank->n.updatec;updatec|Somen->letc'=create()ineopsc;List.iter(funn->n.updatec')(n::H.elsc.heap);copsc;finishc;executec'|None->eopsc;copsc;finishcinupdatecletexecutec=ifc.overtheninvalid_argerr_step_executedelseexecutecletfind_unfinishednl=(* find unfinished step in recursive producers. *)letrecauxnext=function(* zig-zag breadth-first search. *)|[]->ifnext=[]thennilelseaux[]next|[]::todo->auxnexttodo|nl::todo->findnexttodonlandfindnexttodo=function|[]->auxnexttodo|n::nl->ifnotn.stamp.overthenn.stampelsefind(n.producers()::next)todonlinaux[][nl]endmoduleNode=structletdelayed_rank=delayed_rankletmin_rank=min_intletmax_rank=delayed_rank-1letnop_=()letno_producers()=[]letcreater={rank=r;stamp=Step.nil;update=nop;retain=nop;producers=no_producers;deps=Wa.create0}letrem_depnn'=Wa.remn.depsn'letadd_depnn'=Wa.scan_addn.depsn'lethas_depn=not(Wa.is_emptyn.deps)letdepsn=Wa.fold(funaccd->d::acc)[]n.depsletbindnpu=n.producers<-p;n.update<-uletstop?(strong=false)n=ifnotstrongthenbeginn.producers<-no_producers;n.update<-nop;Wa.clearn.deps;endelsebeginletrecloopnextto_rem=function|[]->beginmatchnextwith|(to_rem,prods)::next->loopnextto_remprods|[]->()end|n::todo->rem_depnto_rem;(* N.B. rem_dep could be combined with has_dep *)ifn.rank=min_rank(* is a primitive *)||has_depnthenloopnextto_remtodoelsebeginletprods=n.producers()inn.producers<-no_producers;n.update<-nop;Wa.clearn.deps;loop((n,prods)::next)to_remtodoendinletproducers=n.producers()inn.producers<-no_producers;n.update<-nop;Wa.clearn.deps;loop[]nproducersendletset_ranknr=n.rank<-rletrmin=createmin_rankletrmaxnn'=ifn.rank>n'.rankthennelsen'letrsuccn=ifn.rank=delayed_rankthenmin_rankelseifn.rank<max_rankthenn.rank+1elseinvalid_argerr_max_rankletrsucc2nn'=letr=rsuccninletr'=rsuccn'inifr>r'thenrelser'(* Rank updates currently only increases ranks. If this is problematic
udpate ranks orthodoxly by taking the succ of the max of n.producers.
Note that rank update stops at delayed nodes (otherwise we would
loop and blow the ranks). *)letupdate_ranknr=(* returns true iff n's rank increased. *)letrecaux=function|[]->()|n::todo->letupdatetodod=ifn.rank<d.rank||n.rank=delayed_rankthentodoelse(d.rank<-rsuccn;d::todo)inaux(Wa.foldupdatetodon.deps)inifr>n.rankthen(n.rank<-r;aux[n];true)elsefalseend(* Shortcuts *)letrsucc=Node.rsuccletrsucc2=Node.rsucc2letrmax=Node.rmax(* Event value, creation and update *)letevalm=match!(m.ev)withSomev->v|None->assertfalseletemutrank={ev=refNone;enode=Node.createrank}leteventmpu=Node.bindm.enodepu;Emutmleteupdatevmc=letclearv()=v:=Noneinm.ev:=Somev;Step.add_copc(clearm.ev);Step.add_depscm.enode(* Signal value, creation and update *)letsvalm=matchm.svwithSomev->v|None->assertfalseletsmutrankeq={sv=None;eq=eq;snode=Node.createrank}letsignal?impu=Node.bindm.snodepu;beginmatchiwithSome_asv->m.sv<-v|None->()end;beginmatchStep.find_unfinished(m.snode.producers())with|cwhenc==Step.nil->m.snode.updateStep.nil|c->Step.addcm.snodeend;Smutmletsupdatevmc=matchm.svwith|Somev'when(m.eqvv')->()|Some_->m.sv<-Somev;ifc!=Step.nilthenStep.add_depscm.snode|None->m.sv<-Somev(* init. without init value. *)moduleE=structtype'at='aeventletadd_depmn=Node.add_depm.enoden;if!(m.ev)<>NonethenStep.addm.enode.stampnletsendm?stepv=matchstepwith(* sends an event occurence. *)|Somec->ifc.overtheninvalid_argerr_step_executedelseifnotm.enode.stamp.overtheninvalid_argerr_event_scheduledelsem.enode.stamp<-c;eupdatevmc|None->letc=Step.create()inm.enode.stamp<-c;eupdatevmc;Step.executec(* Basics *)letnever=Neverletcreate()=letm=emutNode.min_rankinEmutm,sendmletretainec=matchewith|Never->invalid_argerr_retain_never|Emutm->letc'=m.enode.retainin(m.enode.retain<-c);(`Rc')letstop?strong=functionNever->()|Emutm->Node.stop?strongm.enodeletequalee'=matche,e'with|Never,Never->true|Never,_|_,Never->false|Emutm,Emutm'->m==m'lettrace?(iff=Consttrue)te=matchiffwith|Constfalse->e|Consttrue->beginmatchewith|Never->e|Emutm->letm'=emut(rsuccm.enode)inletrecp()=[m.enode]anduc=letv=evalmintv;eupdatevm'cinadd_depmm'.enode;eventm'puend|Smutmc->matchewith|Never->Never|Emutm->letm'=emut(rsucc2mc.snodem.enode)inletrecp()=[mc.snode;m.enode]anduc=match!(m.ev)with|None->()(* mc updated. *)|Somev->if(svalmc)thentv;eupdatevm'cinNode.add_depmc.snodem'.enode;add_depmm'.enode;eventm'pu(* Transforming and filtering *)letonce=function|Never->Never|Emutm->letm'=emut(rsuccm.enode)inletrecp()=[m.enode]anduc=Node.rem_depm.enodem'.enode;eupdate(evalm)m'c;Node.stopm'.enodeinadd_depmm'.enode;eventm'puletdrop_once=function|Never->Never|Emutm->letm'=emut(rsuccm.enode)inletrecp()=[m.enode]anduc=(* first update. *)letu'c=eupdate(evalm)m'cin(* subsequent updates. *)Node.bindm'.enodepu'inadd_depmm'.enode;eventm'puletappef=function|Never->Never|Emutm->matchefwith|Never->Never|Emutmf->letm'=emut(rsucc2m.enodemf.enode)inletrecp()=[m.enode;mf.enode]anduc=match!(mf.ev),!(m.ev)with|None,_|_,None->()|Somef,Somev->eupdate(fv)m'cinadd_depmm'.enode;add_depmfm'.enode;eventm'puletmapf=function|Never->Never|Emutm->letm'=emut(rsuccm.enode)inletrecp()=[m.enode]anduc=eupdate(f(evalm))m'cinadd_depmm'.enode;eventm'puletstampev=matchewith|Never->Never|Emutm->letm'=emut(rsuccm.enode)inletrecp()=[m.enode]anduc=eupdatevm'cinadd_depmm'.enode;eventm'puletfilterpred=function|Never->Never|Emutm->letm'=emut(rsuccm.enode)inletrecp()=[m.enode]anduc=letv=evalminifpredvtheneupdatevm'celse()inadd_depmm'.enode;eventm'puletfmapfm=function|Never->Never|Emutm->letm'=emut(rsuccm.enode)inletrecp()=[m.enode]anduc=matchfm(evalm)withSomev->eupdatevm'c|None->()inadd_depmm'.enode;eventm'puletdiffd=function|Never->Never|Emutm->letm'=emut(rsuccm.enode)inletlast=refNoneinletrecp()=[m.enode]anduc=letv=evalminmatch!lastwith|None->last:=Somev|Somev'->last:=Somev;eupdate(dvv')m'cinadd_depmm'.enode;eventm'puletchanges?(eq=(=))=function|Never->Never|Emutm->letm'=emut(rsuccm.enode)inletlast=refNoneinletrecp()=[m.enode]anduc=letv=evalminmatch!lastwith|None->last:=Somev;eupdatevm'c|Somev'->last:=Somev;ifeqvv'then()elseeupdatevm'cinadd_depmm'.enode;eventm'puletonc=function|Never->Never|Emutmase->matchcwith|Consttrue->e|Constfalse->Never|Smutmc->letm'=emut(rsucc2m.enodemc.snode)inletrecp()=[m.enode;mc.snode]anduc=match!(m.ev)with|None->()(* mc updated. *)|Some_->if(svalmc)theneupdate(evalm)m'celse()inadd_depmm'.enode;Node.add_depmc.snodem'.enode;eventm'puletwhen_=onletdismissc=function|Never->Never|Emutmase->matchcwith|Never->e|Emutmc->letm'=emut(rsucc2mc.enodem.enode)inletrecp()=[mc.enode;m.enode]anduc=match!(mc.ev)with|Some_->()|None->eupdate(evalm)m'cinadd_depmcm'.enode;add_depmm'.enode;eventm'puletuntilc=function|Never->Never|Emutmase->matchcwith|Never->e|Emutmc->letm'=emut(rsucc2m.enodemc.enode)inletrecp()=[m.enode;mc.enode]inletuc=match!(mc.ev)with|None->eupdate(evalm)m'c|Some_->Node.rem_depm.enodem'.enode;Node.rem_depmc.enodem'.enode;Node.stopm'.enodeinadd_depmm'.enode;add_depmcm'.enode;eventm'pu(* Accumulating *)letaccumefi=matchefwith|Never->Never|Emutm->letm'=emut(rsuccm.enode)inletacc=refiinletrecp()=[m.enode]anduc=acc:=(evalm)!acc;eupdate!accm'cinadd_depmm'.enode;eventm'puletfoldfi=function|Never->Never|Emutm->letm'=emut(rsuccm.enode)inletacc=refiinletrecp()=[m.enode]anduc=acc:=f!acc(evalm);eupdate!accm'cinadd_depmm'.enode;eventm'pu(* Combining *)letoccursm=!(m.ev)<>Noneletfind_muts_and_next_rankel=letrecauxaccmax=function|[]->List.revacc,rsuccmax|(Emutm)::l->aux(m::acc)(rmaxmaxm.enode)l|Never::l->auxaccmaxlinaux[]Node.rminelletselectel=letemuts,r=find_muts_and_next_rankelinletm'=emutrinletrecp()=List.rev_map(funm->m.enode)emutsanduc=tryeupdate(eval(List.findoccursemuts))m'cwith|Not_found->assertfalseinList.iter(funm->add_depmm'.enode)emuts;eventm'puletmergefael=letrecfoldfacc=function|m::lwhenoccursm->foldf(facc(evalm))l|m::l->foldfaccl|[]->accinletemuts,r=find_muts_and_next_rankelinletm'=emutrinletrecp()=List.rev_map(funm->m.enode)emutsanduc=eupdate(foldfaemuts)m'cinList.iter(funm->add_depmm'.enode)emuts;eventm'puletswitche=function|Never->e|Emutms->letr=matchewith|Emutm->rsucc2m.enodems.enode|Never->rsuccms.enodeinletm'=emutrinletsrc=refein(* current event source. *)letrecp()=match!srcwith|Emutm->[m.enode;ms.enode]|Never->[ms.enode]anduc=match!(ms.ev)with|None->(match!srcwith(* only src occurs. *)|Emutm->eupdate(evalm)m'c|Never->assertfalse)|Somee->beginmatch!srcwith|Emutm->Node.rem_depm.enodem'.enode|Never->()end;src:=e;matchewith|Never->ignore(Node.update_rankm'.enode(rsuccms.enode))|Emutm->Node.add_depm.enodem'.enode;ifNode.update_rankm'.enode(rsucc2m.enodems.enode)thenbegin(* Rank increased because of m. Thus m may stil
update and we may be rescheduled. If it happens
we'll be in the other branch without any harm
but some redundant computation. *)Step.allow_reschedulem'.enode;Step.rebuildc;endelse(* No rank increase, m already updated if needed. *)(match!(m.ev)withSomev->eupdatevm'c|None->())in(matchewithEmutm->add_depmm'.enode|Never->());add_depmsm'.enode;eventm'puletfixf=letm=emutNode.delayed_rankinlete=eventm(fun()->[])(fun_->assertfalse)inmatchfewith|Never,r->r|Emutm',r->ifm'.enode.rank=Node.delayed_ranktheninvalid_argerr_fix;letrecp()=[(* avoid cyclic dep. *)]anduc=(* N.B. c is the next step. *)letclearv()=v:=Noneinm.ev:=Some(evalm');Step.add_eopc(clearm.ev);(* vs. add_cop for regular events. *)Step.add_depscm.enodeinNode.bindm.enodepu;add_depm'm.enode;r(* Lifting *)letl1=mapletl2fe0e1=matche0,e1with|Never,_->Never|_,Never->Never|Emutm0,Emutm1->letr=rsucc2m0.enodem1.enodeinletm'=emutrinletrecp()=[m0.enode;m1.enode]inletuc=match!(m0.ev),!(m1.ev)with|None,_|_,None->()|Somev0,Somev1->eupdate(fv0v1)m'cinadd_depm0m'.enode;add_depm1m'.enode;eventm'puletl3fe0e1e2=matche0,e1,e2with|Never,_,_->Never|_,Never,_->Never|_,_,Never->Never|Emutm0,Emutm1,Emutm2->letr=rsucc(rmax(rmaxm0.enodem1.enode)m2.enode)inletm'=emutrinletrecp()=[m0.enode;m1.enode;m2.enode]inletuc=match!(m0.ev),!(m1.ev),!(m2.ev)with|None,_,_|_,None,_|_,_,None->()|Somev0,Somev1,Somev2->eupdate(fv0v1v2)m'cinadd_depm0m'.enode;add_depm1m'.enode;add_depm2m'.enode;eventm'puletl4fe0e1e2e3=matche0,e1,e2,e3with|Never,_,_,_->Never|_,Never,_,_->Never|_,_,Never,_->Never|_,_,_,Never->Never|Emutm0,Emutm1,Emutm2,Emutm3->letr=rsucc(rmax(rmaxm0.enodem1.enode)(rmaxm2.enodem3.enode))inletm'=emutrinletrecp()=[m0.enode;m1.enode;m2.enode;m3.enode]inletuc=match!(m0.ev),!(m1.ev),!(m2.ev),!(m3.ev)with|None,_,_,_|_,None,_,_|_,_,None,_|_,_,_,None->()|Somev0,Somev1,Somev2,Somev3->eupdate(fv0v1v2v3)m'cinadd_depm0m'.enode;add_depm1m'.enode;add_depm2m'.enode;add_depm3m'.enode;eventm'puletl5fe0e1e2e3e4=matche0,e1,e2,e3,e4with|Never,_,_,_,_->Never|_,Never,_,_,_->Never|_,_,Never,_,_->Never|_,_,_,Never,_->Never|_,_,_,_,Never->Never|Emutm0,Emutm1,Emutm2,Emutm3,Emutm4->letr=rsucc(rmax(rmax(rmaxm0.enodem1.enode)(rmaxm2.enodem3.enode))m4.enode)inletm'=emutrinletrecp()=[m0.enode;m1.enode;m2.enode;m3.enode;m4.enode]inletuc=match!(m0.ev),!(m1.ev),!(m2.ev),!(m3.ev),!(m4.ev)with|None,_,_,_,_|_,None,_,_,_|_,_,None,_,_|_,_,_,None,_|_,_,_,_,None->()|Somev0,Somev1,Somev2,Somev3,Somev4->eupdate(fv0v1v2v3v4)m'cinadd_depm0m'.enode;add_depm1m'.enode;add_depm2m'.enode;add_depm3m'.enode;add_depm4m'.enode;eventm'puletl6fe0e1e2e3e4e5=matche0,e1,e2,e3,e4,e5with|Never,_,_,_,_,_->Never|_,Never,_,_,_,_->Never|_,_,Never,_,_,_->Never|_,_,_,Never,_,_->Never|_,_,_,_,Never,_->Never|_,_,_,_,_,Never->Never|Emutm0,Emutm1,Emutm2,Emutm3,Emutm4,Emutm5->letr=rsucc(rmax(rmax(rmaxm0.enodem1.enode)(rmaxm2.enodem3.enode))(rmaxm4.enodem5.enode))inletm'=emutrinletrecp()=[m0.enode;m1.enode;m2.enode;m3.enode;m4.enode;m5.enode;]inletuc=match!(m0.ev),!(m1.ev),!(m2.ev),!(m3.ev),!(m4.ev),!(m5.ev)with|None,_,_,_,_,_|_,None,_,_,_,_|_,_,None,_,_,_|_,_,_,None,_,_|_,_,_,_,None,_|_,_,_,_,_,None->()|Somev0,Somev1,Somev2,Somev3,Somev4,Somev5->eupdate(fv0v1v2v3v4v5)m'cinadd_depm0m'.enode;add_depm1m'.enode;add_depm2m'.enode;add_depm3m'.enode;add_depm4m'.enode;add_depm5m'.enode;eventm'pu(* Stdlib support *)moduleOption=structletsomee=map(funv->Somev)eletvalue?defaulte=matchdefaultwith|None->fmap(funv->v)e|Some(Constdv)->map(functionNone->dv|Somev->v)e|Some(Smutms)->matchewith|Never->Never|Emutm->letm'=emut(rsucc2m.enodems.snode)inletrecp()=[m.enode;ms.snode]anduc=match!(m.ev)with|None->()(* ms updated. *)|SomeNone->eupdate(svalms)m'c|SomeSomev->eupdatevm'cinadd_depmm'.enode;Node.add_depms.snodem'.enode;eventm'puendendmoduleS=structtype'at='asignalletset_svalvmc=m.sv<-Somev;Step.add_depscm.snodeletsetm?stepv=(* starts an update step. *)ifm.eq(svalm)vthen()elsematchstepwith|Somec->ifc.overtheninvalid_argerr_step_executedelseifnotm.snode.stamp.overtheninvalid_argerr_signal_scheduledelsem.snode.stamp<-c;m.sv<-Somev;Step.add_depscm.snode|None->letc=Step.create()inm.snode.stamp<-c;m.sv<-Somev;Step.add_depscm.snode;Step.executecletend_of_step_add_dep?(post_add_op=fun()->())~stop_if_stoppedmm'=(* In some combinators, when the semantics of event m' is such
that it should not occur in the (potential) step it is created,
we add the dependency [m'] to signal [m] only via an end of
step operation to avoid being scheduled in the step. *)matchStep.find_unfinished(m.snode.producers())with|cwhenc==Step.nil->Node.add_depm.snodem'.enode;post_add_op();|c->letadd_dep()=ifm.snode.update==Node.nopthen(* m stopped in step *)(ifstop_if_stoppedthenNode.stopm'.enode)elsebeginignore(Node.update_rankm'.enode(rsuccm.snode));Node.add_depm.snodem'.enode;post_add_op();endinStep.add_eopcadd_dep(* Basics *)letconstv=Constvletcreate?(eq=(=))v=letm=smutNode.min_rankeqinm.sv<-Somev;Smutm,setmletretainsc=matchswith|Const_->invalid_argerr_retain_cst_sig|Smutm->letc'=m.snode.retaininm.snode.retain<-c;(`Rc')leteq_fun=functionConst_->None|Smutm->Somem.eqletvalue=function|Constv|Smut{sv=Somev}->v|Smut{sv=None}->failwitherr_sig_undefletstop?strong=function|Const_->()|Smutm->matchm.svwith|Some_->Node.stop?strongm.snode|None->(* The signal was dynamically created and didn't update yet. Add the
stop as an end of step operation. *)matchStep.find_unfinished(m.snode.producers())with|cwhenc==Step.nil->assertfalse|c->letstop()=Node.stop?strongm.snodeinStep.add_eopcstopletequal?(eq=(=))ss'=matchs,s'with|Constv,Constv'->eqvv'|Const_,_|_,Const_->false|Smutm,Smutm'->m==m'lettrace?(iff=consttrue)ts=matchiffwith|Constfalse->s|Consttrue->beginmatchswith|Constv->tv;s|Smutm->letm'=smut(rsuccm.snode)m.eqinletrecp()=[m.snode]inletuc=letv=svalmintv;supdatevm'cinNode.add_depm.snodem'.snode;signalm'puend|Smutmc->matchswith|Constv->letm'=smut(rsuccmc.snode)(=)(* we don't care about eq *)inletrecp()=[mc.snode]anduc=if(svalmc)thentv;Node.rem_depmc.snodem'.snode;Node.stopm'.snode;inNode.add_depmc.snodem'.snode;signal~i:vm'pu|Smutm->letm'=smut(rsucc2mc.snodem.snode)m.eqinletrecp()=[mc.snode;m.snode]anduc=letv=svalminmatchm'.svwith|Somev'whenm'.eqvv'->()(* mc updated. *)|_->if(svalmc)thentv;supdatevm'c(* init or diff. *)inNode.add_depmc.snodem'.snode;Node.add_depm.snodem'.snode;signalm'pu(* From events *)lethold?(eq=(=))i=function|Never->Consti|Emutm->letm'=smut(rsuccm.enode)eqinletrecp()=[m.enode]anduc=match!(m.ev)with|None->()(* init. only. *)|Somev->supdatevm'cinE.add_depmm'.snode;signal~im'pu(* Filtering and transforming *)letmap?(eq=(=))f=function|Constv->Const(fv)|Smutm->letm'=smut(rsuccm.snode)eqinletrecp()=[m.snode]anduc=supdate(f(svalm))m'cinNode.add_depm.snodem'.snode;signalm'puletapp?(eq=(=))sfsv=matchsf,svwith|Smutmf,Smutmv->letm'=smut(rsucc2mf.snodemv.snode)eqinletrecp()=[mf.snode;mv.snode]anduc=supdate((svalmf)(svalmv))m'cinNode.add_depmf.snodem'.snode;Node.add_depmv.snodem'.snode;signalm'pu|Constf,Constv->Const(fv)|Constf,sv->map~eqfsv|Smutmf,Constv->letm'=smut(rsuccmf.snode)eqinletrecp()=[mf.snode]anduc=supdate((svalmf)v)m'cinNode.add_depmf.snodem'.snode;signalm'puletfilter?(eq=(=))predi=function|Constvass->ifpredvthenselseConsti|Smutm->letm'=smut(rsuccm.snode)eqinletrecp()=[m.snode]anduc=letv=svalminifpredvthensupdatevm'celse()inNode.add_depm.snodem'.snode;signal~im'puletfmap?(eq=(=))fmi=function|Constv->(matchfmvwithSomev'->Constv'|None->Consti)|Smutm->letm'=smut(rsuccm.snode)eqinletrecp()=[m.snode]anduc=matchfm(svalm)withSomev->supdatevm'c|None->()inNode.add_depm.snodem'.snode;signal~im'puletdiffd=function|Const_->Never|Smutm->letm'=emut(rsuccm.snode)inletlast=refNoneinletrecp()=[m.snode]anduc=letv=svalminmatch!lastwith|Somev'->last:=Somev;eupdate(dvv')m'c|None->assertfalseinletpost_add_op()=last:=Some(svalm)inend_of_step_add_dep~post_add_op~stop_if_stopped:truemm';eventm'puletchanges=function|Const_->Never|Smutm->letm'=emut(rsuccm.snode)inletrecp()=[m.snode]anduc=eupdate(svalm)m'cinend_of_step_add_dep~stop_if_stopped:truemm';eventm'puletsamplefe=function|Constv->E.map(funev->fevv)e|Smutms->matchewith|Never->Never|Emutme->letm'=emut(rsucc2me.enodems.snode)inletrecp()=[me.enode;ms.snode]anduc=match!(me.ev)with|None->()(* ms updated *)|Somev->eupdate(fv(svalms))m'cinE.add_depmem'.enode;Node.add_depms.snodem'.enode;eventm'puleton?(eq=(=))cis=matchcwith|Consttrue->s|Constfalse->Consti|Smutmc->matchswith|Constv->letm'=smut(rsuccmc.snode)eqinletrecp()=[mc.snode]anduc=if(svalmc)thensupdatevm'celse()inNode.add_depmc.snodem'.snode;signal~im'pu|Smutms->letm'=smut(rsucc2mc.snodems.snode)eqinletrecp()=[mc.snode;ms.snode]anduc=if(svalmc)thensupdate(svalms)m'celse()inNode.add_depmc.snodem'.snode;Node.add_depms.snodem'.snode;signal~im'puletwhen_=onletdismiss?(eq=(=))cis=matchcwith|Never->s|Emutmc->matchswith|Constv->letm'=smut(rsuccmc.enode)eqinletrecp()=[mc.enode]anduc=match!(mc.ev)with|Some_->()|None->supdatevm'cinNode.add_depmc.enodem'.snode;signal~im'pu|Smutms->letm'=smut(rsucc2mc.enodems.snode)eqinletrecp()=[mc.enode;ms.snode]anduc=match!(mc.ev)with|Some_->()|None->supdate(svalms)m'cinNode.add_depmc.enodem'.snode;Node.add_depms.snodem'.snode;signal~im'pu(* Accumulating *)letaccum?(eq=(=))efi=matchefwith|Never->Consti|Emutm->letm'=smut(rsuccm.enode)eqinletrecp()=[m.enode]anduc=match!(m.ev)with|None->()(* init only. *)|Somev->supdate(v(svalm'))m'cinE.add_depmm'.snode;signal~im'puletfold?(eq=(=))fi=function|Never->Consti|Emutm->letm'=smut(rsuccm.enode)eqinletrecp()=[m.enode]anduc=match!(m.ev)with|None->()(* init only. *)|Somev->supdate(f(svalm')v)m'cinE.add_depmm'.snode;signal~im'pu(* Combining *)letmerge?(eq=(=))fasl=letrmax'acc=functionConst_->acc|Smutm->rmaxaccm.snodeinletnodesacc=functionConst_->acc|Smutm->m.snode::accinletmergerfa=functionConstv->fav|Smutm->fa(svalm)inletm'=smut(rsucc(List.fold_leftrmax'Node.rminsl))eqinletrecp()=List.fold_leftnodes[]slanduc=supdate(List.fold_left(mergerf)asl)m'cinletdep=functionConst_->()|Smutm->Node.add_depm.snodem'.snodeinList.iterdepsl;signalm'puletswitch?(eq=(=))=function|Consts->s|Smutmss->letdummy=smutNode.min_rankeqinletsrc=ref(Smutdummy)in(* dummy is overwritten by sig. init *)letm'=smut(rsuccmss.snode)eqinletrecp()=match!srcwith|Smutm->[mss.snode;m.snode]|Const_->[mss.snode]anduc=if(svalmss)==!srcthen(* ss didn't change, !src did *)beginmatch!srcwith|Smutm->supdate(svalm)m'c|Const_->()(* init only. *)endelse(* ss changed *)beginbeginmatch!srcwith|Smutm->Node.rem_depm.snodem'.snode|Const_->()end;letnew_src=svalmssinsrc:=new_src;matchnew_srcwith|Constv->ignore(Node.update_rankm'.snode(rsuccmss.snode));supdatevm'c|Smutm->Node.add_depm.snodem'.snode;ifc==Step.nilthenbeginignore(Node.update_rankm'.snode(rsucc2m.snodemss.snode));(* Check if the init src is in a step. *)matchStep.find_unfinished[m.snode]with|cwhenc==Step.nil->supdate(svalm)m'c|c->Step.addcm'.snodeendelseifNode.update_rankm'.snode(rsucc2m.snodemss.snode)thenbegin(* Rank increased because of m. Thus m may still
update and we need to reschedule. Next time we
will be in the other branch. *)Step.allow_reschedulem'.snode;Step.rebuildc;Step.addcm'.snodeendelse(* No rank increase. m already updated if needed, no need
to reschedule and rebuild the queue. *)supdate(svalm)m'cendinNode.add_depmss.snodem'.snode;(* We add a dep to dummy to avoid a long scan of Wa.rem when we remove
the dep in the [u] function during static init. *)Node.add_depdummy.snodem'.snode;signalm'puletbind?eqssf=switch?eq(map~eq:(==)sfs)letfix?(eq=(=))if=letupdate_delayednpunl=Node.bindnpu;matchStep.find_unfinishednlwith|cwhenc==Step.nil->(* no pertinent occuring step, create a step for update. *)letc=Step.create()inn.updatec;Step.executec|c->Step.addcninletm=smutNode.delayed_rankeqinlets=signal~im(fun()->[])(fun_->())inmatchfswith|Constv,r->letrecp()=[]anduc=supdatevmcinupdate_delayedm.snodepu(Node.depsm.snode);r|Smutm',r->ifm'.snode.rank=Node.delayed_ranktheninvalid_argerr_fix;letrecp()=[(* avoid cyclic dep. *)]anduc=supdate(svalm')mcin(* N.B. c is the next step. *)Node.add_depm'.snodem.snode;update_delayedm.snodepu(m'.snode::Node.depsm.snode);r(* Lifting *)letl1=mapletl2?(eq=(=))fss'=matchs,s'with|Smutm0,Smutm1->letm'=smut(rsucc2m0.snodem1.snode)eqinletrecp()=[m0.snode;m1.snode]anduc=supdate(f(svalm0)(svalm1))m'cinNode.add_depm0.snodem'.snode;Node.add_depm1.snodem'.snode;signalm'pu|Constv,Constv'->Const(fvv')|Constv,Smutm->letm'=smut(rsuccm.snode)eqinletrecp()=[m.snode]anduc=supdate(fv(svalm))m'cinNode.add_depm.snodem'.snode;signalm'pu|Smutm,Constv->letm'=smut(rsuccm.snode)eqinletrecp()=[m.snode]anduc=supdate(f(svalm)v)m'cinNode.add_depm.snodem'.snode;signalm'puletl3?(eq=(=))fs0s1s2=matchs0,s1,s2with|Smutm0,Smutm1,Smutm2->letr=rsucc(rmax(rmaxm0.snodem1.snode)m2.snode)inletm'=smutreqinletrecp()=[m0.snode;m1.snode;m2.snode]anduc=supdate(f(svalm0)(svalm1)(svalm2))m'cinNode.add_depm0.snodem'.snode;Node.add_depm1.snodem'.snode;Node.add_depm2.snodem'.snode;signalm'pu|Constv0,Constv1,Constv2->Const(fv0v1v2)|s0,s1,s2->app~eq(l2~eq:(==)fs0s1)s2letl4?(eq=(=))fs0s1s2s3=matchs0,s1,s2,s3with|Smutm0,Smutm1,Smutm2,Smutm3->letr=rsucc(rmax(rmaxm0.snodem1.snode)(rmaxm2.snodem3.snode))inletm'=smutreqinletrecp()=[m0.snode;m1.snode;m2.snode;m3.snode]anduc=supdate(f(svalm0)(svalm1)(svalm2)(svalm3))m'cinNode.add_depm0.snodem'.snode;Node.add_depm1.snodem'.snode;Node.add_depm2.snodem'.snode;Node.add_depm3.snodem'.snode;signalm'pu|Constv0,Constv1,Constv2,Constv3->Const(fv0v1v2v3)|s0,s1,s2,s3->app~eq(l3~eq:(==)fs0s1s2)s3letl5?(eq=(=))fs0s1s2s3s4=matchs0,s1,s2,s3,s4with|Smutm0,Smutm1,Smutm2,Smutm3,Smutm4->letm=rmaxinletr=rsucc(m(mm0.snodem1.snode)(mm2.snode(mm3.snodem4.snode)))inletm'=smutreqinletrecp()=[m0.snode;m1.snode;m2.snode;m3.snode;m4.snode]anduc=letv=f(svalm0)(svalm1)(svalm2)(svalm3)(svalm4)insupdatevm'cinNode.add_depm0.snodem'.snode;Node.add_depm1.snodem'.snode;Node.add_depm2.snodem'.snode;Node.add_depm3.snodem'.snode;Node.add_depm4.snodem'.snode;signalm'pu|Constv0,Constv1,Constv2,Constv3,Constv4->Const(fv0v1v2v3v4)|s0,s1,s2,s3,s4->app~eq(l4~eq:(==)fs0s1s2s3)s4letl6?(eq=(=))fs0s1s2s3s4s5=matchs0,s1,s2,s3,s4,s5with|Smutm0,Smutm1,Smutm2,Smutm3,Smutm4,Smutm5->letm=rmaxinletm=m(mm0.snode(mm1.snodem2.snode))(mm3.snode(mm4.snodem5.snode))inletm'=smut(rsuccm)eqinletrecp()=[m0.snode;m1.snode;m2.snode;m3.snode;m4.snode;m5.snode]anduc=letv=f(svalm0)(svalm1)(svalm2)(svalm3)(svalm4)(svalm5)insupdatevm'cinNode.add_depm0.snodem'.snode;Node.add_depm1.snodem'.snode;Node.add_depm2.snodem'.snode;Node.add_depm3.snodem'.snode;Node.add_depm4.snodem'.snode;Node.add_depm5.snodem'.snode;signalm'pu|Constv0,Constv1,Constv2,Constv3,Constv4,Constv5->Const(fv0v1v2v3v4v5)|s0,s1,s2,s3,s4,s5->app~eq(l5~eq:(==)fs0s1s2s3s4)s5moduleBool=structletstdlib_not=notletone=Consttrueletzero=Constfalseleteq:bool->bool->bool=(=)letnots=l1~eqstdlib_notslet(&&)ss'=l2~eq(&&)ss'let(||)ss'=l2~eq(||)ss'letedges=changessletedge_detectedge=function|Const_->Never|Smutm->letm'=emut(rsuccm.snode)inletrecp()=[m.snode]anduc=if(svalm)=edgetheneupdate()m'cinend_of_step_add_dep~stop_if_stopped:truemm';eventm'puletrises=edge_detecttruesletfalls=edge_detectfalsesletflipb=function|Never->Constb|Emutm->letm'=smut(rsuccm.enode)(=)inletrecp()=[m.enode]anduc=match!(m.ev)with|None->()|Some_->supdate(stdlib_not(svalm'))m'cinE.add_depmm'.snode;signal~i:bm'puendmoduleInt=structletzero=Const0letone=Const1letminus_one=Const(-1)leteq:int->int->bool=(=)let(~-)s=l1~eq(~-)sletsuccs=l1~eqsuccsletpreds=l1~eqpredslet(+)ss'=l2~eq(+)ss'let(-)ss'=l2~eq(-)ss'let(*)ss'=l2~eq(*)ss'let(mod)ss'=l2~eq(mod)ss'letabss=l1~eqabssletmax_int=constmax_intletmin_int=constmin_intlet(land)ss'=l2~eq(land)ss'let(lor)ss'=l2~eq(lor)ss'let(lxor)ss'=l2~eq(lxor)ss'letlnots=l1~eqlnotslet(lsl)ss'=l2~eq(lsl)ss'let(lsr)ss'=l2~eq(lsr)ss'let(asr)ss'=l2~eq(asr)ss'endmoduleFloat=structletzero=Const0.letone=Const1.letminus_one=Const(-1.)leteq:float->float->bool=(=)let(~-.)s=l1~eq(~-.)slet(+.)ss'=l2~eq(+.)ss'let(-.)ss'=l2~eq(-.)ss'let(*.)ss'=l2~eq(*.)ss'let(/.)ss'=l2~eq(/.)ss'let(**)ss'=l2~eq(**)ss'letsqrts=l1~eqsqrtsletexps=l1~eqexpsletlogs=l1~eqlogsletlog10s=l1~eqlog10sletcoss=l1~eqcossletsins=l1~eqsinslettans=l1~eqtansletacoss=l1~eqacossletasins=l1~eqasinsletatans=l1~eqatansletatan2ss'=l2~eqatan2ss'letcoshs=l1~eqcoshsletsinhs=l1~eqsinhslettanhs=l1~eqtanhsletceils=l1~eqceilsletfloors=l1~eqfloorsletabs_floats=l1~eqabs_floatsletmod_floatss'=l2~eqmod_floatss'letfrexps=l1~eq:(=)frexpsletldexpss'=l2~eqldexpss'letmodfs=l1~eq:(=)modfsletfloats=l1~eqfloatsletfloat_of_ints=l1~eqfloat_of_intslettruncates=l1~eq:Int.eqtruncatesletint_of_floats=l1~eq:Int.eqint_of_floatsletinfinity=constinfinityletneg_infinity=constneg_infinityletnan=constnanletmax_float=constmax_floatletmin_float=constmin_floatletepsilon_float=constepsilon_floatletclassify_floats=l1~eq:(=)classify_floatsendmodulePair=structletpair?eqss'=l2?eq(funxy->x,y)ss'letfst?eqs=l1?eqfstsletsnd?eqs=l1?eqsndsendmoduleOption=structletnone=ConstNoneletsomes=leteq=matcheq_funswith|None->None|Someeq->leteqvv'=matchv,v'with|Somev,Somev'->eqvv'|_->assertfalseinSomeeqinmap?eq(funv->Somev)sletvalue?(eq=(=))~defaults=matchswith|Const(Somev)->Constv|ConstNone->beginmatchdefaultwith|`Alwaysd->d|`Initd->beginmatchdwith|Constd->Constd|Smutmd->matchStep.find_unfinished[md.snode]with|cwhenc==Step.nil->Const(svalmd)|c->letm'=smut(rsuccmd.snode)eqinletrecp()=[md.snode]anduc=Node.rem_depmd.snodem'.snode;supdate(svalmd)m'c;Node.stopm'.snodeinNode.add_depmd.snodem'.snode;signalm'puendend|Smutm->matchdefaultwith|`Init(Constd)->fmap~eq(funv->v)ds|`Always(Constd)->map~eq(functionNone->d|Somev->v)s|`Init(Smutmd)->beginmatchStep.find_unfinished[md.snode]with|cwhenc==Step.nil->letm'=smut(rsuccm.snode)eqinletrecp()=[m.snode]anduc=matchsvalmwith|Somev->supdatevm'c|None->()inNode.add_depm.snodem'.snode;signal~i:(svalmd)m'pu|c->letm'=smut(rsucc2m.snodemd.snode)eqinletrecp()=[m.snode]in(* subsequent updates *)letuc=matchsvalmwith|Somev->supdatevm'c|None->()inletrecp_first()=[m.snode;md.snode]in(* first update *)letu_firstc=Node.rem_depmd.snodem'.snode;beginmatchsvalmwith|None->supdate(svalmd)m'c|Somev->supdatevm'cend;Node.bindm'.snodepuinNode.add_depm.snodem'.snode;Node.add_depmd.snodem'.snode;signalm'p_firstu_firstend|`Always(Smutmd)->letm'=smut(rsucc2m.snodemd.snode)eqinletrecp()=[m.snode;md.snode]inletuc=matchsvalmwith|Somev->supdatevm'c|None->supdate(svalmd)m'cinNode.add_depm.snodem'.snode;Node.add_depmd.snodem'.snode;signalm'puendmoduleCompare=structleteq=Bool.eqlet(=)ss'=l2~eq(=)ss'let(<>)ss'=l2~eq(<>)ss'let(<)ss'=l2~eq(<)ss'let(>)ss'=l2~eq(>)ss'let(<=)ss'=l2~eq(<=)ss'let(>=)ss'=l2~eq(>=)ss'letcomparess'=l2~eq:Int.eqcomparess'let(==)ss'=l2~eq(==)ss'let(!=)ss'=l2~eq(!=)ss'end(* Combinator specialization *)moduletypeEqType=sigtype'atvalequal:'at->'at->boolendmoduletypeS=sigtype'avvalcreate:'av->'avsignal*(?step:step->'av->unit)valequal:'avsignal->'avsignal->boolvalhold:'av->'avevent->'avsignalvalapp:('a->'bv)signal->'asignal->'bvsignalvalmap:('a->'bv)->'asignal->'bvsignalvalfilter:('av->bool)->'av->'avsignal->'avsignalvalfmap:('a->'bvoption)->'bv->'asignal->'bvsignalvalwhen_:boolsignal->'av->'avsignal->'avsignalvaldismiss:'bevent->'av->'avsignal->'avsignalvalaccum:('av->'av)event->'av->'avsignalvalfold:('av->'b->'av)->'av->'bevent->'avsignalvalmerge:('av->'b->'av)->'av->'bsignallist->'avsignalvalswitch:'avsignalsignal->'avsignalvalbind:'bsignal->('b->'avsignal)->'avsignalvalfix:'av->('avsignal->'avsignal*'b)->'bvall1:('a->'bv)->('asignal->'bvsignal)vall2:('a->'b->'cv)->('asignal->'bsignal->'cvsignal)vall3:('a->'b->'c->'dv)->('asignal->'bsignal->'csignal->'dvsignal)vall4:('a->'b->'c->'d->'ev)->('asignal->'bsignal->'csignal->'dsignal->'evsignal)vall5:('a->'b->'c->'d->'e->'fv)->('asignal->'bsignal->'csignal->'dsignal->'esignal->'fvsignal)vall6:('a->'b->'c->'d->'e->'f->'gv)->('asignal->'bsignal->'csignal->'dsignal->'esignal->'fsignal->'gvsignal)endmoduleMake(Eq:EqType)=structtype'av='aEq.tleteq=Eq.equalletcreatev=create~eqvletequalss'=equal~eqss'letholdve=hold~eqveletappsfsv=app~eqsfsvletmapfs=map~eqfsletfilterpredi=filter~eqprediletfmapfmi=fmap~eqfmiletwhen_cis=when_~eqcisletdismisscs=dismiss~eqcsletaccumefi=accum~eqefiletfoldfi=fold~eqfiletmergefasl=merge~eqfaslletswitchs=switch~eqsletbindssf=bind~eqssfletfixf=fix~eqfletl1=mapletl2fss'=l2~eqfss'letl3fs0s1s2=l3~eqfs0s1s2letl4fs0s1s2s3=l4~eqfs0s1s2s3letl5fs0s1s2s3s4=l5~eqfs0s1s2s3s4letl6fs0s1s2s3s4s5=l6~eqfs0s1s2s3s4s5endmoduleSpecial=structmoduleSb=Make(structtype'at=boolletequal=Bool.eqend)moduleSi=Make(structtype'at=intletequal=Int.eqend)moduleSf=Make(structtype'at=floatletequal=Float.eqend)endend(*---------------------------------------------------------------------------
Copyright (c) 2009 The react programmers
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
---------------------------------------------------------------------------*)