12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788open!ImportopenStd_internaltype('a,'b)fn='a->'bmoduleResult=structtype'at=|Rvalof'a|Exptofexnletreturn=function|Rvalv->v|Expte->raisee;;letcapturefx=tryRval(fx)with|Sys.Breakase->raisee|e->Expte;;endletunitf=letl=Lazy.from_funfinfun()->Lazy.forcel;;letunbounded(typea)?(hashable=Hashtbl.Hashable.poly)f=letcache=letmoduleA=Hashable.Make_plain_and_derive_hash_fold_t(structtypet=alet{Hashtbl.Hashable.hash;compare;sexp_of_t}=hashableend)inA.Table.create()~size:0in(* Allocate this closure at the call to [unbounded], not at each call to the memoized
function. *)letreally_call_farg=Result.capturefarginfunarg->Result.return(Hashtbl.findi_or_addcachearg~default:really_call_f);;(* the same but with a bound on cache size *)letlru(typea)?(hashable=Hashtbl.Hashable.poly)~max_cache_sizef=ifmax_cache_size<=0thenfailwithf"Memo.lru: max_cache_size of %i <= 0"max_cache_size();letmoduleCache=Hash_queue.Make(structtypet=alet{Hashtbl.Hashable.hash;compare;sexp_of_t}=hashableend)inletcache=Cache.create()infunarg->Result.return(matchCache.lookup_and_move_to_backcacheargwith|Someresult->result|None->letresult=Result.capturefarginCache.enqueue_back_exncacheargresult;(* eject least recently used cache entry *)ifCache.lengthcache>max_cache_sizethenignore(Cache.dequeue_front_exncache:_Result.t);result);;letgeneral?hashable?cache_size_boundf=matchcache_size_boundwith|None->unbounded?hashablef|Somen->lru?hashable~max_cache_size:nf;;letof_comparable(typeindex)(moduleM:Comparable.S_plainwithtypet=index)f=letm=refM.Map.emptyinfun(x:M.t)->letv=matchMap.find!mxwith|Somev->v|None->letv=Result.capturefxinm:=Map.set!m~key:x~data:v;vinResult.returnv;;