123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803(**************************************************************************)(* This file is part of the Codex semantics library *)(* (patricia-tree sub-component). *)(* *)(* Copyright (C) 2024-2025 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* You can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1. *)(* *)(* It is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file LICENSE). *)(**************************************************************************)(** All signatures used in this library *)openInts(** {1 Nodes} *)(** Nodes are the underlying representation used to build a patricia-tree.
The module type specifies the constructors they must provide, and a common
interface used for pattern-matching. *)(** This module explains how a node is stored in memory, with
functions to create and view nodes.
@canonical PatriciaTree.NODE *)moduletypeNODE=sig(** We use a uniform type ['map view] to pattern match on maps and sets
The actual types ['map t] can be a bit different from ['map view]
to allow for more efficient representations, but {!val:view} should be a
constant time operation for quick conversions. *)(** {2 Types} *)type'keykey(** The type of keys. *)type('key,'map)value(** The type of value, which depends on the type of the key and the type of the map. *)type'mapt(** The type of the map, which is parameterized by a type. *)(** {2 Constructors: build values} *)valempty:'mapt(** The empty map *)valleaf:'keykey->('key,'map)value->'mapt(** A singleton leaf, similar to {!BASE_MAP.singleton} *)valbranch:prefix:intkey->branching_bit:mask->tree0:'mapt->tree1:'mapt->'mapt(** A branch node.
{b This shouldn't be called externally unless you know what you're doing!}
Doing so could easily break the data structure's invariants.
When called, it assumes that:
- Neither [tree0] nor [tree1] should be empty.
- [branching_bit] should have a single bit set
- [prefix] should be normalized (bits below [branching_bit] set to zero)
- All elements of [tree0] should have their [to_int] start by
[prefix] followed by 0 at position [branching_bit]).
- All elements of [tree1] should have their [to_int] start by
[prefix] followed by 0 at position [branching_bit]). *)(** {2 Destructors: access the value} *)(** This makes the map nodes accessible to the pattern matching
algorithm; this corresponds 1:1 to the {!SimpleNode}
implementation. This just needs to be copy-and-pasted for every
node type. *)type'mapview=private|Empty:'mapview(** Can happen only at the toplevel: there is no empty interior node. *)|Branch:{prefix:intkey;branching_bit:mask;tree0:'mapt;tree1:'mapt;}->'mapview(** Same constraints as {!branch}:
- [branching_bit] contains only one bit set; the corresponding mask is (branching_bit - 1).
- [prefix] is normalized: the bits below the [branching_bit] are set to zero
(i.e. [prefix & (branching_bit - 1) = 0]).
- All elements of [tree0] should have their [to_int] start by
[prefix] followed by 0 at position [branching_bit]).
- All elements of [tree1] should have their [to_int] start by
[prefix] followed by 0 at position [branching_bit]). *)|Leaf:{key:'keykey;value:('key,'map)value;}->'mapview(** A key -> value mapping. *)valis_empty:'mapt->bool(** Check if the map is empty. Should be constant time. *)valview:'at->'aview(** Convert the map to a view. Should be constant time. *)end(** Associate a unique number to each node, so they can be used as keys in sets or maps.
@canonical PatriciaTree.NODE_WITH_ID *)moduletypeNODE_WITH_ID=sigincludeNODE(** @closed *)valto_int:'at->int(** Unique number for each node.
This is not {{!hash_consed}hash-consing}.
Equal nodes created separately will have different
identifiers. On the flip side, nodes with equal identifiers will always be
physically equal. *)end(** Hash-consed nodes also associate a unique number to each node,
Unlike {!NODE_WITH_ID}, they also check before instanciating the node whether
a similar node already exists. This results in slightly slower constructors
(they perform an extra hash-table lookup), but allows for constant time
equality and comparison.
See {!hash_consed} for a details on strengths and limits of hash-consing.
@since v0.10.0
@canonical PatriciaTree.HASH_CONSED_NODE *)moduletypeHASH_CONSED_NODE=sigincludeNODE(** @closed *)valto_int:'at->int(** Returns a unique number for each map, the {{!hash_consed}hash-consed} identifier of the map.
Unlike {!NODE_WITH_ID.to_int}, hash-consing ensures that maps
which contain the same keys (compared by {!KEY.to_int}) and values (compared
by {!HASHED_VALUE.polyeq}) will always be physically equal
and have the same identifier.
Maps with the same identifier are also physically equal:
[to_int m1 = to_int m2] implies [m1 == m2].
Note that when using physical equality as {!HASHED_VALUE.polyeq}, some
maps of different types [a t] and [b t] may be given the same identifier.
See the end of the documentation of {!HASHED_VALUE.polyeq} for details. *)valequal:'at->'at->bool(** Constant time equality using the {{!hash_consed}hash-consed} nodes identifiers.
This is equivalent to physical equality.
Two nodes are equal if their trees contain the same bindings,
where keys are compared by {!KEY.to_int} and values are compared by
{!HASHED_VALUE.polyeq}. *)valcompare:'at->'at->int(** Constant time comparison using the {{!hash_consed}hash-consed} node identifiers.
This order is fully arbitrary, but it is total and can be used to sort nodes.
It is based on node ids which depend on the order in which the nodes where created
(older nodes having smaller ids).
One useful property of this order is that
child nodes will always have a smaller identifier than their parents. *)end(** A {!NODE} along with its {!NODE_WITH_FIND.find} function.
This is the minimal argument to the {!HETEROGENEOUS_MAP.WithForeign} functors
@since v0.11.0
@canonical PatriciaTree.NODE_WITH_FIND *)moduletypeNODE_WITH_FIND=sigincludeNODE(** @closed *)valfind:'keykey->'mapt->('key,'map)value(** [find key map] returns the value associated with [key] in [map] if present.
@raises Not_found if [key] is absent from map *)valfind_opt:'keykey->'mapt->('key,'map)valueoption(** Same as {!find}, but returns [None] for Not_found *)end(** {1 Map signatures} *)(** {2 Base map} *)(** Base map signature: a generic ['b map] storing bindings
of ['a key] to [('a,'b) values].
All maps and set are a variation of this type,
sometimes with a simplified interface.
- {!HETEROGENEOUS_MAP} is just a {!BASE_MAP} with a functor {!HETEROGENEOUS_MAP.WithForeign}
for building operations that operate on two maps of different base types;
- {!MAP} specializes the interface for non-generic keys ([key] instead of ['a key]);
- {!HETEROGENEOUS_SET} specializes {!BASE_MAP} for sets ([('a,'b) value = unit]) and
removes the value argument from most operations;
- {!SET} specializes {!HETEROGENEOUS_SET} further by making elements (keys)
non-generic ([elt] instead of ['a elt]).
@canonical PatriciaTree.BASE_MAP *)moduletypeBASE_MAP=sigincludeNODE_WITH_FIND(** @open *)(** Existential wrapper for the ['a] parameter in a ['a key], [('a,'map) value] pair *)type'mapkey_value_pair=KeyValue:'akey*('a,'map)value->'mapkey_value_pair(** {1 Basic functions} *)valunsigned_min_binding:'at->'akey_value_pair(** [unsigned_min_binding m] is minimal binding [KeyValue(k,v)] of the map,
using the {{!unsigned_lt}unsigned order} on {!KEY.to_int}.
@raises Not_found if the map is empty *)valunsigned_max_binding:'at->'akey_value_pair(** [unsigned_max_binding m] is maximal binding [KeyValue(k,v)] of the map,
using the {{!unsigned_lt}unsigned order} on {!KEY.to_int}.
@raises Not_found if the map is empty *)valsingleton:'akey->('a,'b)value->'bt(** Create a map with a single binding. *)valcardinal:'at->int(** The size of the map, O(n) complexity *)valis_singleton:'at->'akey_value_pairoption(** [is_singleton m] returns [Some(KeyValue(k,v))] if and only if
[m] contains a unique binding [k->v]. *)valmem:'keykey->'mapt->bool(** [mem key map] returns [true] iff [key] is bound in [map], O(log(n)) complexity. *)valremove:'keykey->'mapt->'mapt(** Returns a map with the element removed, O(log(n)) complexity.
Returns a physically equal map if the element is absent. *)valpop_unsigned_minimum:'mapt->('mapkey_value_pair*'mapt)option(** [pop_unsigned_minimum m] returns [None] if [is_empty m], or [Some(key,value,m')] where
[(key,value) = unsigned_min_binding m] and [m' = remove m key].
Uses the {{!unsigned_lt}unsigned order} on {!KEY.to_int}.
O(log(n)) complexity. *)valpop_unsigned_maximum:'mapt->('mapkey_value_pair*'mapt)option(** [pop_unsigned_maximum m] returns [None] if [is_empty m], or [Some(key,value,m')] where
[(key,value) = unsigned_max_binding m] and [m' = remove m key].
Uses the {{!unsigned_lt}unsigned order} on {!KEY.to_int}.
O(log(n)) complexity. *)valinsert:'akey->(('a,'map)valueoption->('a,'map)value)->'mapt->'mapt(** [insert key f map] modifies or insert an element of the map; [f]
takes [None] if the value was not previously bound, and [Some old]
where [old] is the previously bound value otherwise. The function
preserves physical equality when possible. O(log(n))
complexity.
Preserves physical equality if the new value is physically equal to the old. *)valupdate:'akey->(('a,'map)valueoption->('a,'map)valueoption)->'mapt->'mapt(** [update key f map] modifies, insert, or remove an element from
the map; [f] takes [None] if the value was not previously bound, and
[Some old] where [old] is the previously bound value otherwise. The
function preserves physical equality when possible. It returns
None if the element should be removed O(log(n)) complexity.
Preserves physical equality if the new value is physically equal to the old. *)valadd:'keykey->('key,'map)value->'mapt->'mapt(** Unconditionally adds a value in the map (independently from
whether the old value existed). O(log(n)) complexity.
Preserves physical equality if the new value is physically equal to the old. *)(** {1 Iterators} *)valsplit:'keykey->'mapt->'mapt*('key,'map)valueoption*'mapt(** [split key map] splits the map into:
- submap of [map] whose keys are smaller than [key]
- value associated to [key] (if present)
- submap of [map] whose keys are bigger than [key]
Where the order is given by the {{!unsigned_lt}unsigned order} on {!KEY.to_int}. *)type'mappolyiter={f:'a.'akey->('a,'map)value->unit;}[@@unboxed]valiter:'mappolyiter->'mapt->unit(** [iter f m] calls [f.f] on all bindings of [m],
in the {{!unsigned_lt}unsigned order} on {!KEY.to_int} *)type('acc,'map)polyfold={f:'a.'akey->('a,'map)value->'acc->'acc}[@@unboxed]valfold:('acc,'map)polyfold->'mapt->'acc->'acc(** [fold f m acc] returns [f.f key_n value_n (... (f.f key_1 value_1 acc))]
where [(key_1, value_1) ... (key_n, value_n)] are the bindings of [m], in
the {{!unsigned_lt}unsigned order} on {!KEY.to_int}. *)type('acc,'map)polyfold2={f:'a.'akey->('a,'map)value->('a,'map)value->'acc->'acc}[@@unboxed]valfold_on_nonequal_inter:('acc,'map)polyfold2->'mapt->'mapt->'acc->'acc(** [fold_on_nonequal_inter f m1 m2 acc] returns
[f.f key_n value1_n value2n (... (f.f key_1 value1_1 value2_1 acc))] where
[(key_1, value1_1, value2_1) ... (key_n, value1_n, value2_n)] are the
bindings that exist in both maps ([m1 ∩ m2]) whose values are physically different.
Calls to [f.f] are performed in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)type('acc,'map)polyfold2_union={f:'a.'akey->('a,'map)valueoption->('a,'map)valueoption->'acc->'acc}[@@unboxed]valfold_on_nonequal_union:('acc,'map)polyfold2_union->'mapt->'mapt->'acc->'acc(** [fold_on_nonequal_union f m1 m2 acc] returns
[f.f key_n value1_n value2n (... (f.f key_1 value1_1 value2_1 acc))] where
[(key_1, value1_1, value2_1) ... (key_n, value1_n, value2_n)] are the
bindings that exists in either map ([m1 ∪ m2]) whose values are physically
different.
Calls to [f.f] are performed in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)type'mappolypredicate={f:'a.'akey->('a,'map)value->bool;}[@@unboxed]valfilter:'mappolypredicate->'mapt->'mapt(** [filter f m] returns the submap of [m] containing the bindings [k->v]
such that [f.f k v = true].
[f.f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)valfor_all:'mappolypredicate->'mapt->bool(** [for_all f m] checks that [f] holds on all bindings of [m].
Short-circuiting. *)(** In the following, the *no_share function allows taking arguments
of different types (but cannot share subtrees of the map), while
the default functions attempt to preserve and benefit from
sharing the subtrees (using physical equality to detect
sharing). *)type('map1,'map2)polymap={f:'a.('a,'map1)value->('a,'map2)value;}[@@unboxed]valmap:('map,'map)polymap->'mapt->'maptvalmap_no_share:('map1,'map2)polymap->'map1t->'map2t(** [map f m] and [map_no_share f m] replace all bindings [(k,v)] by [(k, f.f v)].
Bindings are examined in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)type('map1,'map2)polymapi={f:'a.'akey->('a,'map1)value->('a,'map2)value;}[@@unboxed]valmapi:('map,'map)polymapi->'mapt->'maptvalmapi_no_share:('map1,'map2)polymapi->'map1t->'map2t(** [mapi f m] and [mapi_no_share f m] replace all bindings [(k,v)] by [(k, f.f k v)].
Bindings are examined in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)type('map1,'map2)polyfilter_map={f:'a.'akey->('a,'map1)value->('a,'map2)valueoption;}[@@unboxed]valfilter_map:('map,'map)polyfilter_map->'mapt->'maptvalfilter_map_no_share:('map1,'map2)polyfilter_map->'map1t->'map2t(** [filter_map m f] and [filter_map_no_share m f] remove the bindings
[(k,v)] for which [f.f k v] is [None], and replaces the bindings [(k,v)]
for which [f.f k v] is [Some v'] by [(k,v')].
Bindings are examined in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)type'mappolypretty={f:'a.Format.formatter->'akey->('a,'map)value->unit}[@@unboxed]valpretty:?pp_sep:(Format.formatter->unit->unit)->'mappolypretty->Format.formatter->'mapt->unit(** Pretty-prints a map using the given formatter.
[pp_sep] is called once between each binding,
it defaults to {{: https://v2.ocaml.org/api/Format.html#VALpp_print_cut}[Format.pp_print_cut]}.
Bindings are printed in the {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)(** {1:functions_on_pairs Functions on pairs of maps} *)(** This section regroups functions that act on pairs of maps.
{b These functions are where Patricia trees offer substantial speedup
compared to Stdlib's Maps:}
- We can often avoid exploring physically equal subtrees
(for equality tests, inclusion tests, union, intersection, difference).
This yields important performance gains when combining maps that derive
from a common ancestor or when using {!hash_consed} maps which have a lot
of elements in common.
- We can also avoid visiting a subtree when merging with [Empty] (for union,
intersection and difference).
To do so safely, we have
specialized versions of these functions that assume properties
of the function parameter (reflexive relation, idempotent
operation, etc.)
When we cannot enjoy these properties, our functions explicitly
say so (with a nonreflexive or nonidempotent prefix). The names
are a bit long, but having these names avoids using an
ineffective code by default, by forcing to know and choose
between the fast and slow version.
In general, the fast versions of these function will be on [O(log n + d)] where
[n] is the size of the maps being joined and [d] the size of their difference
(number of keys bound in both maps to non-physically equal values). The slow
version is [O(n)].
Many of these are high-order functions, taking as argument a function [f]
that operates on elements.
Due to {{: https://ocaml.org/manual/5.2/polymorphism.html#s%3Ahigher-rank-poly}restrictions with higher-order polymorphism},
we need to wrap the function [f] in a record, which has a single field [f].
These is what the [polyXXX] types are for.*)(** {2 Comparing two maps} *)(** Functions for equality, inclusion, and test for disjointness. *)type('map1,'map2)polysame_domain_for_all2={f:'a.'akey->('a,'map1)value->('a,'map2)value->bool;}[@@unboxed]valreflexive_same_domain_for_all2:('map,'map)polysame_domain_for_all2->'mapt->'mapt->bool(** [reflexive_same_domain_for_all2 f m1 m2] is true if and only if
- [m1] and [m2] have the same domain (set of keys)
- for all bindings [(k, v1)] in [m1] and [(k, v2)] in [m2], [f.f k v1 v2] holds
{b Assumes} [f.f] is reflexive, i.e. [f.f k v v = true] to skip calls to equal subtrees.
Calls [f.f] in ascending {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
Exits early if the domains mismatch or if [f.f] returns false.
It is useful to implement equality on maps:
{[
# let equal m1 m2 = MyMap.reflexive_same_domain_for_all2
{ f = fun _ v1 v2 -> MyValue.equal v1 v2}
m1 m2;;
val equal : 'a MyMap.t -> 'a MyMap.t -> bool = <fun>
]} *)valnonreflexive_same_domain_for_all2:('map1,'map2)polysame_domain_for_all2->'map1t->'map2t->bool(** [nonreflexive_same_domain_for_all2 f m1 m2] is the same as
{!reflexive_same_domain_for_all2}, but doesn't assume [f.f] is reflexive.
It thus calls [f.f] on every binding, in ascending {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
Exits early if the domains mismatch or if [f.f] returns false. *)valreflexive_subset_domain_for_all2:('map,'map)polysame_domain_for_all2->'mapt->'mapt->bool(** [reflexive_subset_domain_for_all2 f m1 m2] is true if and only if
- [m1]'s domain is a subset of [m2]'s. (all keys defined in [m1] are also defined in [m2])
- for all bindings [(k, v1)] in [m1] and [(k, v2)] in [m2], [f.f k v1 v2] holds
{b Assumes} [f.f] is reflexive, i.e. [f.f k v v = true] to skip calls to equal subtrees.
Calls [f.f] in ascending {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
Exits early if the domains mismatch.
It is useful to implement inclusion test on maps:
{[
# let is_submap m1 m2 = MyMap.reflexive_subset_domain_for_all2
{ f = fun _ v1 v2 -> MyValue.equal v1 v2}
m1 m2;;
val is_submap : 'a MyMap.t -> 'a MyMap.t -> bool = <fun>
]} *)type'mappolycompare={f:'a.'akey->('a,'map)value->('a,'map)value->int;}[@@unboxed]valreflexive_compare:'apolycompare->'at->'at->int(** [reflexive_compare f m1 m2] is an order relation on maps.
[m1] and [m2] are equal (return [0]) if they have the same domain and for all bindings
[(k,v)] in [m1], [(k,v')] in [m2], we have [f v v' = 0].
[m1] is considered striclty smaller than [m2] (return a negative integer)
when the first difference (lowest key in the {{!unsigned_lt}unsigned order} of {!KEY.to_int})
is either a shared binding [(k,v)] in [m1], [(k,v')] in [m2] with [f v v' < 0],
or a binding that only occurs in [m2].
Assumes that [f v v = 0].
@since v0.11.0 *)valdisjoint:'at->'at->bool(** [disjoint m1 m2] is [true] iff [m1] and [m2] have disjoint domains *)(** {2:combining_maps Combining two maps} *)(** We provide many functions that operate on pairs of maps for computing intersection,
union, difference... Here is a short summary of what each of one returns when
applied to two maps [m1] and [m2]. Here [y] is physically the same value in
[m1] and [m2].
{t
| Keys | [a] | [b] | [c] | [d] |
|:-----|:---:|:---:|:---:|:---:|
| [m1] | [x] | [y] | [z] | |
| [m2] | | [y] | [u] | [v] |
| {{!idempotent_union}[idempotent_union f m1 m2]} | [x] | [y] | [f c z u] | [v] |
| {{!slow_merge}[slow_merge f m1 m2]}{^ \[1\]}{^ \[2\]} | [f a x _] | [f b y y] | [f c z u] | [f d _ v] |
| {{!idempotent_inter}[idempotent_inter f m1 m2]} | | [y] | [f c z u] | |
| {{!idempotent_inter_filter}[idempotent_inter_filter f m1 m2]}{^ \[1\]} | | [y] | [f c z u] | |
| {{!nonidempotent_inter_no_share}[nonidempotent_inter_no_share f m1 m2]} | | [f b y y] | [f c z u] | |
| {{!difference}[difference f m1 m2]}{^ \[1\]} | [x] | | [f c z u] | |
| {{!symmetric_difference}[symmetric_difference f m1 m2]}{^ \[1\]} | [x] | | [f c z u] | [v] |
}
{b \[1\]}: Here [f] returns an optional value, returning [None] removes the binding.
{b \[2\]}: Here the function [f] actually takes [option] as arguments,
omitted for brevity. [_] is [None]. *)type('map1,'map2,'map3)polyunion={f:'a.'akey->('a,'map1)value->('a,'map2)value->('a,'map3)value;}[@@unboxed]validempotent_union:('a,'a,'a)polyunion->'at->'at->'at(** [idempotent_union f map1 map2] returns a map whose keys is the
union of the keys of [map1] and [map2]. [f.f] is used to combine
the values of keys mapped in both maps.
{b Assumes} [f.f] idempotent (i.e. [f key value value == value])
[f.f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
[f.f] is never called on physically equal values.
Preserves physical equality as much as possible.
Complexity is O(log(n)*Delta) where Delta is the number of
different keys between [map1] and [map2]. *)type('map1,'map2,'map3)polyinter={f:'a.'akey->('a,'map1)value->('a,'map2)value->('a,'map3)value;}[@@unboxed]validempotent_inter:('a,'a,'a)polyinter->'at->'at->'at(** [idempotent_inter f map1 map2] returns a map whose keys is the
intersection of the keys of [map1] and [map2]. [f.f] is used to combine
the values a key is mapped in both maps.
{b Assumes} [f.f] idempotent (i.e. [f.f key value value == value])
[f.f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
[f.f] is never called on physically equal values.
Preserves physical equality as much as possible.
Complexity is O(log(n)*Delta) where Delta is the number of
different keys between [map1] and [map2]. *)valnonidempotent_inter_no_share:('a,'b,'c)polyinter->'at->'bt->'ct(** [nonidempotent_inter_no_share f map1 map2] is the same as {!idempotent_inter}
but doesn't preverse physical equality, doesn't assume [f.f] is idempotent,
and can change the type of values. [f.f] is called on every shared binding.
[f.f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
O(n) complexity *)type('map1,'map2,'map3)polyinterfilter={f:'a.'akey->('a,'map1)value->('a,'map2)value->('a,'map3)valueoption;}[@@unboxed]validempotent_inter_filter:('a,'a,'a)polyinterfilter->'at->'at->'at(** [idempotent_inter_filter f map1 map2] is the same as {!idempotent_inter}
but [f.f] can return [None] to remove a binding from the resutling map. *)type('map1,'map2,'map3)polymerge={f:'a.'akey->('a,'map1)valueoption->('a,'map2)valueoption->('a,'map3)valueoption;}[@@unboxed]valslow_merge:('map1,'map2,'map3)polymerge->'map1t->'map2t->'map3t(** This is the same as {{: https://ocaml.org/api/Map.S.html#VALmerge}Stdlib.Map.S.merge} *)type('a,'b)polydifference=('a,'b,'a)polyinterfiltervalsymmetric_difference:('a,'a)polydifference->'at->'at->'at(** [symmetric_difference f map1 map2] returns a map comprising of the bindings
of [map1] that aren't in [map2], and the bindings of [map2] that aren't in [map1].
Bindings that are both in [map1] and [map2], but with non-physically equal values
are passed to [f.f]. If [f.f] returns [Some v] then [v] is used as the new value,
otherwise the binding is dropped.
{b Assumes} [f.f] is none on equal values (i.e. [f.f key value value == None])
[f.f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
[f.f] is never called on physically equal values.
Complexity is [O(log n + d)] where [n] is the size of the maps, and [d] the
size of the difference.
@since v0.11.0 *)valdifference:('a,'a)polydifference->'at->'at->'at(** [difference f map1 map2] returns the map containing the bindings of [map1]
that aren't in [map2]. For keys present in both maps but with different
values, [f.f] is called. If it returns [Some v], then binding [k,v] is kept,
else [k] is dropped.
{b Assumes} [f.f] is [None] on the diagonal: [f.f k v v = None].
[f.f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
[f.f] is never called on physically equal values.
@since v0.11.0 *)(** {2 Min/max of intersection} *)(** Existential wrapper for a key with two values
@since v0.11.0 *)type('a,'b)key_value_value=KeyValueValue:'kkey*('k,'a)value*('k,'b)value->('a,'b)key_value_valuevalmin_binding_inter:'at->'bt->('a,'b)key_value_valueoption(** [min_binding_inter m1 m2] is the minimal binding of the intersection.
I.E. the {{!KeyValueValue}[KeyValueValue(k,v1,v2)]} such that
[(k,v1)] is in [m1], [(k,v2)] is in [m2], and [k] is minimal using
the {{!unsigned_lt}unsigned order} on keys.
Returns [None] if and only if the intersection is empty.
It is rougthly equivalent to calling {!unsigned_min_binding} on
{{!nonidempotent_inter_no_share}[nonindempotent_inter_no_share f m1 m2]},
but can be faster.
@since v0.11.0 *)valmax_binding_inter:'at->'bt->('a,'b)key_value_valueoption(** [max_binding_inter m1 m2] is the same as {!min_binding_inter}, but returns
the maximum key instead of the minimum.
@since v0.11.0 *)(** {1 Conversion functions} *)valto_seq:'at->'akey_value_pairSeq.t(** [to_seq m] iterates the whole map, in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)valto_rev_seq:'at->'akey_value_pairSeq.t(** [to_rev_seq m] iterates the whole map, in decreasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)valadd_seq:'akey_value_pairSeq.t->'at->'at(** [add_seq s m] adds all bindings of the sequence [s] to [m] in order. *)valof_seq:'akey_value_pairSeq.t->'at(** [of_seq s] creates a new map from the bindings of [s].
If a key is bound multiple times in [s], the latest binding is kept *)valof_list:'akey_value_pairlist->'at(** [of_list l] creates a new map from the bindings of [l].
If a key is bound multiple times in [l], the latest binding is kept *)valto_list:'at->'akey_value_pairlist(** [to_list m] returns the bindings of [m] as a list, in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)end(** {2 Heterogeneous maps and sets} *)(** Maps and sets with generic keys ['a key] and values [('a,'b) value] *)moduletypeHETEROGENEOUS_MAP=sig(** This is the same as {!MAP}, but with simple type [key] being replaced by type
constructor ['a key] and ['b value] being replaced by [('a,'b) value].
The main changes from {!MAP} are:
- The type of {!key} is replaced by a type constructor ['k key].
Because of that, most higher-order arguments require higher-ranking
polymorphism, and we provide records that allows to
pass them as arguments (e.g. {!polyiter}, {!polymap}, {!polyunion}, etc.)
- The type of the map ({!type:t}) is still parameterized by an argument (['m t])
- The type of {!type:value} depend on both the type of the key and the
type of the map, hence the type [('k,'m) value].
- The type of some return values, like key-value pairs, must be
concealed existentially, hence the {!KeyValue} constructor.
@canonical PatriciaTree.HETEROGENEOUS_MAP *)includeBASE_MAP(** @closed *)(** Operation with maps/set of different types.
[Map2] must use the same {!KEY.to_int} function. *)moduleWithForeign(Map2:NODE_WITH_FINDwithtype'akey='akey):sigtype('map1,'map2)polyinter_foreign={f:'a.'akey->('a,'map1)value->('a,'map2)Map2.value->('a,'map1)value}[@@unboxed]valnonidempotent_inter:('a,'b)polyinter_foreign->'at->'bMap2.t->'at(** Like {!BASE_MAP.idempotent_inter}. Tries to preserve physical equality on the first argument when possible. *)type('map2,'map1)polyfilter_map={f:'a.'akey->('a,'map2)Map2.value->('a,'map1)valueoption;}[@@unboxed]valfilter_map_no_share:('map2,'map1)polyfilter_map->'map2Map2.t->'map1t(** Like {!BASE_MAP.filter_map_no_share}, but allows to transform a foreigh map into the current one. *)type('map1,'map2)polyupdate_multiple={f:'a.'akey->('a,'map1)valueoption->('a,'map2)Map2.value->('a,'map1)valueoption}[@@unboxed]valupdate_multiple_from_foreign:'bMap2.t->('a,'b)polyupdate_multiple->'at->'at(** This is equivalent to multiple calls to {!update}, but more efficient.
[update_multiple_from_foreign m_from f m_to] is the same as calling
[update k {f=fun v_to -> f.f k v_to v_from} m_to]
on all bindings [(k, v_from)] of [m_from],
i.e. [update_multiple_from_foreign m_from f m_to] calls [f.f] on every
key of [m_from], says if the corresponding value also exists in [m_to],
and adds or remove the element in [m_to] depending on the value of [f.f].
[f.f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
O(size(m_from) + size(m_to)) complexity. *)type('map1,'map2,'map3)polyupdate_multiple_inter={f:'a.'akey->('a,'map1)value->('a,'map2)Map2.value->('a,'map3)valueoption}[@@unboxed]valupdate_multiple_from_inter_with_foreign:'bMap2.t->('a,'b,'a)polyupdate_multiple_inter->'at->'at(** [update_multiple_from_inter_with_foreign m_from f m_to] is the same as
{!update_multiple_from_foreign}, except that instead of updating for all
keys in [m_from], it only updates for keys that are both in [m_from] and
[m_to]. *)type('map1,'map2)polydifference=('map1,'map2,'map1)polyupdate_multiple_intervaldifference:('a,'b)polydifference->'at->'bMap2.t->'at(** [difference f map1 map2] returns the map containing the bindings of [map1]
that aren't in [map2]. For keys present in both maps but with different
values, [f.f] is called. If it returns [Some v], then binding [k,v] is kept,
else [k] is dropped.
[f.f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
This is the same as {!BASE_MAP.difference} but allows the second map to
be of a different type.
@since v0.11.0 *)(** Existential wrapper for a key with two values
@since v0.11.0 *)type('a,'b)key_value_value=KeyValueValue:'kkey*('k,'a)value*('k,'b)Map2.value->('a,'b)key_value_valuevalmin_binding_inter:'at->'bMap2.t->('a,'b)key_value_valueoption(** [min_binding_inter m1 m2] is the minimal binding of the intersection.
I.E. the {{!KeyValueValue}[KeyValueValue(k,v1,v2)]} such that
[(k,v1)] is in [m1], [(k,v2)] is in [m2], and [k] is minimal using
the {{!unsigned_lt}unsigned order} on keys.
Returns [None] if and only if the intersection is empty.
It is rougthly equivalent to calling {!unsigned_min_binding} on
{{!nonidempotent_inter}[nonindempotent_inter f m1 m2]},
but can be faster.
@since v0.11.0 *)valmax_binding_inter:'at->'bMap2.t->('a,'b)key_value_valueoption(** [max_binding_inter m1 m2] is the same as {!min_binding_inter}, but returns
the maximum key instead of the minimum.
@since v0.11.0 *)endendmoduletypeHETEROGENEOUS_SET=sig(** A set containing different keys, very similar to
{!SET}, but with simple type [elt] being replaced by type
constructor ['a elt].
The main changes from {!SET} are:
- The type of {!elt} is replaced by a type constructor ['k elt].
Because of that, most higher-order arguments require higher-ranking
polymorphism, and we provide records that allows to
pass them as arguments (e.g. {!polyfold}, {!polypretty}, etc.)
- The type of some return values, must be concealed existentially,
hence the {!Any} constructor.
@canonical PatriciaTree.HETEROGENEOUS_SET *)type'aelt(** Elements of the set *)(** Underlying basemap, for cross map/set operations *)moduleBaseMap:HETEROGENEOUS_MAPwithtype'akey='aeltandtype(_,_)value=unittypet=unitBaseMap.t(** The type of our set *)type'akey='aelt(** Alias for elements, for compatibility with other PatriciaTrees *)(** Existential wrapper for set elements. *)typeany_elt=Any:'aelt->any_elt(** {1 Basic functions} *)valempty:t(** The empty set *)valis_empty:t->bool(** [is_empty st] is [true] if [st] contains no elements, [false] otherwise *)valmem:'aelt->t->bool(** [mem elt set] is [true] if [elt] is contained in [set], O(log(n)) complexity. *)valadd:'aelt->t->t(** [add elt set] adds element [elt] to the [set].
Preserves physical equality if [elt] was already present.
O(log(n)) complexity. *)valsingleton:'aelt->t(** [singleton elt] returns a set containing a single element: [elt] *)valcardinal:t->int(** the size of the set (number of elements), O(n) complexity. *)valis_singleton:t->any_eltoption(** [is_singleton set] is [Some (Any elt)] if [set] is [singleton elt] and [None] otherwise. *)valremove:'aelt->t->t(** [remove elt set] returns a set containing all elements of [set] except [elt].
Returns a value physically equal to [set] if [elt] is not present. *)valunsigned_min_elt:t->any_elt(** The minimal element if non empty, according to the
{{!unsigned_lt}unsigned order} on elements.
@raises Not_found *)valunsigned_max_elt:t->any_elt(** The maximal element if non empty, according to the
{{!unsigned_lt}unsigned order} on elements.
@raises Not_found *)valpop_unsigned_minimum:t->(any_elt*t)option(** [pop_unsigned_minimum s] is [Some (elt, s')] where [elt = unsigned_min_elt s] and [s' = remove elt s]
if [s] is non empty.
Uses the {{!unsigned_lt}unsigned order} on elements. *)valpop_unsigned_maximum:t->(any_elt*t)option(** [pop_unsigned_maximum s] is [Some (elt, s')] where [elt = unsigned_max_elt s] and [s' = remove elt s]
if [s] is non empty.
Uses the {{!unsigned_lt}unsigned order} on elements. *)(** {1 Functions on pairs of sets} *)valunion:t->t->t(** [union a b] is the set union of [a] and [b], i.e. the set containing all
elements that are either in [a] or [b]. *)valinter:t->t->t(** [inter a b] is the set intersection of [a] and [b], i.e. the set containing all
elements that are in both [a] or [b]. *)valdisjoint:t->t->bool(** [disjoint a b] is [true] if [a] and [b] have no elements in common. *)valequal:t->t->bool(** [equal a b] is [true] if [a] and [b] contain the same elements. *)valcompare:t->t->int(** [compare s1 s2] is an order on setss.
[s1] and [s2] are equal if they contain the same bindings (compare by {!KEY.to_int}).
[s1] is strictly smaller than [s2] if the first difference (in the order of {!KEY.to_int})
is an element that appears in [s2] but not in [s1].
@since v0.11.0 *)valsubset:t->t->bool(** [subset a b] is [true] if all elements of [a] are also in [b]. *)valsplit:'aelt->t->t*bool*t(** [split elt set] returns [s_lt, present, s_gt] where
[s_lt] contains all elements of [set] smaller than [elt], [s_gt]
all those greater than [elt], and [present] is [true] if [elt] is in [set].
Uses the {{!unsigned_lt}unsigned order} on elements. *)valdiff:t->t->t(** [diff s1 s2] is the set of all elements of [s1] that aren't in [s2].
@since v0.11.0 *)valmin_elt_inter:t->t->any_eltoption(** [min_elt_inter s1 s2] is {!unsigned_min_elt} of {{!inter}[inter s1 s2]}, but
faster as it does not require computing the whole intersection.
Returns [None] when the intersection is empty.
@since v0.11.0 *)valmax_elt_inter:t->t->any_eltoption(** [max_elt_inter s1 s2] is {!unsigned_max_elt} of {{!inter}[inter s1 s2]}, but
faster as it does not require computing the whole intersection.
Returns [None] when the intersection is empty.
@since v0.11.0 *)(** {1 Iterators} *)typepolyiter={f:'a.'aelt->unit;}[@@unboxed]valiter:polyiter->t->unit(** [iter f set] calls [f.f] on all elements of [set], in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)typepolypredicate={f:'a.'aelt->bool;}[@@unboxed]valfilter:polypredicate->t->t(** [filter f set] is the subset of [set] that only contains the elements that
satisfy [f.f]. [f.f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valfor_all:polypredicate->t->bool(** [for_all f set] is [true] if [f.f] is [true] on all elements of [set].
Short-circuits on first [false]. [f.f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)type'accpolyfold={f:'a.'aelt->'acc->'acc}[@@unboxed]valfold:'accpolyfold->t->'acc->'acc(** [fold f set acc] returns [f.f elt_n (... (f.f elt_1 acc) ...)], where
[elt_1, ..., elt_n] are the elements of [set], in increasing {{!unsigned_lt}unsigned order} of
{!KEY.to_int} *)typepolypretty={f:'a.Format.formatter->'aelt->unit;}[@@unboxed]valpretty:?pp_sep:(Format.formatter->unit->unit)->polypretty->Format.formatter->t->unit(** Pretty prints the set, [pp_sep] is called once between each element,
it defaults to {{: https://v2.ocaml.org/api/Format.html#VALpp_print_cut}[Format.pp_print_cut]} *)(** {1 Conversion functions} *)valto_seq:t->any_eltSeq.t(** [to_seq st] iterates the whole set, in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)valto_rev_seq:t->any_eltSeq.t(** [to_rev_seq st] iterates the whole set, in decreasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)valadd_seq:any_eltSeq.t->t->t(** [add_seq s st] adds all elements of the sequence [s] to [st] in order. *)valof_seq:any_eltSeq.t->t(** [of_seq s] creates a new set from the elements of [s]. *)valof_list:any_eltlist->t(** [of_list l] creates a new set from the elements of [l]. *)valto_list:t->any_eltlist(** [to_list s] returns the elements of [s] as a list, in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)end(** {2 Homogeneous maps and sets} *)(** Same as above, but simple interfaces for non-generic keys. These
are also close to the standard library's interface for sets and maps. *)(** Signature for sets implemented using Patricia trees.
Most of this interface should be shared with {{: https://ocaml.org/api/Set.S.html}[Stdlib.Set.S]}.
@canonical PatriciaTree.SET *)moduletypeSET=sigtypeelt(** The type of elements of the set *)typekey=elt(** Alias for the type of elements, for cross-compatibility with maps *)(** Underlying basemap, for cross map/set operations *)moduleBaseMap:HETEROGENEOUS_MAPwithtype_key=eltandtype(_,_)value=unittypet=unitBaseMap.t(** The set type *)(** {1 Basic functions} *)valempty:t(** The empty set *)valis_empty:t->bool(** [is_empty st] is [true] if [st] contains no elements, [false] otherwise *)valmem:elt->t->bool(** [mem elt set] is [true] if [elt] is contained in [set], O(log(n)) complexity. *)valadd:elt->t->t(** [add elt set] adds element [elt] to the [set].
Preserves physical equality if [elt] was already present.
O(log(n)) complexity. *)valsingleton:elt->t(** [singleton elt] returns a set containing a single element: [elt] *)valcardinal:t->int(** [cardinal set] is the size of the set (number of elements), O(n) complexity. *)valis_singleton:t->eltoption(** [is_singleton set] is [Some (Any elt)] if [set] is [singleton elt] and [None] otherwise. *)valremove:elt->t->t(** [remove elt set] returns a set containing all elements of [set] except [elt].
Returns a value physically equal to [set] if [elt] is not present. *)valunsigned_min_elt:t->elt(** The minimal element (according to the {{!unsigned_lt}unsigned order} on {!KEY.to_int}) if non empty.
@raises Not_found *)valunsigned_max_elt:t->elt(** The maximal element (according to the {{!unsigned_lt}unsigned order} on {!KEY.to_int}) if non empty.
@raises Not_found *)valpop_unsigned_minimum:t->(elt*t)option(** [pop_unsigned_minimum s] is [Some (elt, s')] where [elt = unsigned_min_elt s] and [s' = remove elt s]
if [s] is non empty.
Uses the {{!unsigned_lt}unsigned order} on {!KEY.to_int}. *)valpop_unsigned_maximum:t->(elt*t)option(** [pop_unsigned_maximum s] is [Some (elt, s')] where [elt = unsigned_max_elt s] and [s' = remove elt s]
if [s] is non empty.
Uses the {{!unsigned_lt}unsigned order} on {!KEY.to_int}. *)(** {1 Iterators} *)valiter:(elt->unit)->t->unit(** [iter f set] calls [f] on all elements of [set], in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valfilter:(elt->bool)->t->t(** [filter f set] is the subset of [set] that only contains the elements that
satisfy [f]. [f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valfor_all:(elt->bool)->t->bool(** [for_all f set] is [true] if [f] is [true] on all elements of [set].
Short-circuits on first [false]. [f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valfold:(elt->'acc->'acc)->t->'acc->'acc(** [fold f set acc] returns [f elt_n (... (f elt_1 acc) ...)], where
[elt_1, ..., elt_n] are the elements of [set], in increasing {{!unsigned_lt}unsigned order} of
{!KEY.to_int} *)valsplit:elt->t->t*bool*t(** [split elt set] returns [s_lt, present, s_gt] where
[s_lt] contains all elements of [set] smaller than [elt], [s_gt]
all those greater than [elt], and [present] is [true] if [elt] is in [set].
Uses the {{!unsigned_lt}unsigned order} on {!KEY.to_int}.*)valpretty:?pp_sep:(Format.formatter->unit->unit)->(Format.formatter->elt->unit)->Format.formatter->t->unit(** Pretty prints the set, [pp_sep] is called once between each element,
it defaults to {{: https://v2.ocaml.org/api/Format.html#VALpp_print_cut}[Format.pp_print_cut]} *)(** {1 Functions on pairs of sets} *)valunion:t->t->t(** [union a b] is the set union of [a] and [b], i.e. the set containing all
elements that are either in [a] or [b]. *)valinter:t->t->t(** [inter a b] is the set intersection of [a] and [b], i.e. the set containing all
elements that are in both [a] or [b]. *)valdisjoint:t->t->bool(** [disjoint a b] is [true] if [a] and [b] have no elements in common. *)valequal:t->t->bool(** [equal a b] is [true] if [a] and [b] contain the same elements. *)valcompare:t->t->int(** [compare s1 s2] is an order on setss.
[s1] and [s2] are equal if they contain the same bindings (compare by {!KEY.to_int}).
[s1] is strictly smaller than [s2] if the first difference (in the order of {!KEY.to_int})
is an element that appears in [s2] but not in [s1].
@since v0.11.0 *)valsubset:t->t->bool(** [subset a b] is [true] if all elements of [a] are also in [b]. *)valdiff:t->t->t(** [diff s1 s2] is the set of all elements of [s1] that aren't in [s2].
@since v0.11.0 *)valmin_elt_inter:t->t->eltoption(** [min_elt_inter s1 s2] is {!unsigned_min_elt} of {{!inter}[inter s1 s2]}, but
faster as it does not require computing the whole intersection.
Returns [None] when the intersection is empty.
@since v0.11.0 *)valmax_elt_inter:t->t->eltoption(** [max_elt_inter s1 s2] is {!unsigned_max_elt} of {{!inter}[inter s1 s2]}, but
faster as it does not require computing the whole intersection.
Returns [None] when the intersection is empty.
@since v0.11.0 *)(** {1 Conversion functions} *)valto_seq:t->eltSeq.t(** [to_seq st] iterates the whole set, in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)valto_rev_seq:t->eltSeq.t(** [to_rev_seq st] iterates the whole set, in decreasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)valadd_seq:eltSeq.t->t->t(** [add_seq s st] adds all elements of the sequence [s] to [st] in order. *)valof_seq:eltSeq.t->t(** [of_seq s] creates a new set from the elements of [s]. *)valof_list:eltlist->t(** [of_list l] creates a new set from the elements of [l]. *)valto_list:t->eltlist(** [to_list s] returns the elements of [s] as a list, in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)end(** The typechecker struggles with forall quantification on values if they
don't depend on the first parameter, this wrapping allows our code to pass
typechecking by forbidding overly eager simplification.
Since the type is unboxed, it doesn't introduce any performance overhead.
This is due to a bug in the typechecker, more info on
{{: https://discuss.ocaml.org/t/weird-behaviors-with-first-order-polymorphism/13783} the OCaml discourse post}
and {{: https://github.com/ocaml/ocaml/issues/13292}the github issue}.
@canonical PatriciaTree.snd *)type(_,'b)snd=Sndof'b[@@unboxed](** The signature for maps with a single type for keys and values,
a ['a map] binds [key] to ['a value].
This is slightly more generic than {!MAP}, which just binds to ['a].
It is used for maps that need to restrict their value type, namely {!hash_consed}.
@since v0.10.0
@canonical PatriciaTree.MAP_WITH_VALUE *)moduletypeMAP_WITH_VALUE=sigtypekey(** The type of keys. *)type'at(** A map from [key] to values of type ['a value]. *)type'avalue(** Type for values, this is a divergence from Stdlib's [Map],
but becomes equivalent to it when using {!MAP},
which is just [MAP_WITH_VALUE with type 'a value = 'a].
On the other hand, it allows defining maps with fixed values, which is useful
for hash-consing.
@since v0.10.0 *)(** Underlying basemap, for cross map/set operations *)moduleBaseMap:HETEROGENEOUS_MAPwithtype'at='atandtype_key=keyandtype('a,'b)value=('a,'bvalue)snd(** {1 Basic functions} *)valempty:'at(** The empty map. *)valis_empty:'at->bool(** Test if a map is empty; O(1) complexity. *)valunsigned_min_binding:'at->(key*'avalue)(** Returns the [(key,value)] pair where [Key.to_int key] is minimal (in the
{{!unsigned_lt}unsigned representation} of integers); O(log n) complexity.
@raises Not_found if the map is empty. *)valunsigned_max_binding:'at->(key*'avalue)(** Returns the [(key,value)] pair where [Key.to_int key] is maximal (in the
{{!unsigned_lt}unsigned representation} of integers); O(log n) complexity.
@raises Not_found if the map is empty. *)valsingleton:key->'avalue->'at(** [singleton key value] creates a map with a single binding, O(1) complexity. *)valcardinal:'at->int(** The size of the map. O(n) complexity. *)valis_singleton:'at->(key*'avalue)option(** [is_singleton m] is [Some (k,v)] iff [m] is [singleton k v]. *)valfind:key->'at->'avalue(** Return an element in the map, or raise [Not_found], O(log(n)) complexity. *)valfind_opt:key->'at->'avalueoption(** Return an element in the map, or [None], O(log(n)) complexity. *)valmem:key->'at->bool(** [mem key map] returns [true] if and only if [key] is bound in [map].
O(log(n)) complexity. *)valremove:key->'at->'at(** Returns a map with the element removed, O(log(n)) complexity.
Returns a physically equal map if the element is absent. *)valpop_unsigned_minimum:'at->(key*'avalue*'at)option(** [pop_unsigned_minimum m] returns [None] if [is_empty m], or [Some(key,value,m')] where
[(key,value) = unsigned_min_binding m] and [m' = remove m key]. O(log(n)) complexity.
Uses the {{!unsigned_lt}unsigned order} on {!KEY.to_int}. *)valpop_unsigned_maximum:'at->(key*'avalue*'at)option(** [pop_unsigned_maximum m] returns [None] if [is_empty m], or [Some(key,value,m')] where
[(key,value) = unsigned_max_binding m] and [m' = remove m key]. O(log(n)) complexity.
Uses the {{!unsigned_lt}unsigned order} on {!KEY.to_int}. *)valinsert:key->('avalueoption->'avalue)->'at->'at(** [insert key f map] modifies or insert an element of the map; [f]
takes [None] if the value was not previously bound, and [Some old]
where [old] is the previously bound value otherwise. The function
preserves physical equality when possible. O(log(n))
complexity.
Preserves physical equality if the new value is physically equal to the old. *)valupdate:key->('avalueoption->'avalueoption)->'at->'at(** [update key f map] modifies, insert, or remove an element from
the map; [f] takes [None] if the value was not previously bound, and
[Some old] where [old] is the previously bound value otherwise. The
function preserves physical equality when possible. It returns
None if the element should be removed O(log(n)) complexity.
Preserves physical equality if the new value is physically equal to the old. *)valadd:key->'avalue->'at->'at(** Unconditionally adds a value in the map (independently from
whether the old value existed). O(log(n)) complexity.
Preserves physical equality if the new value is physically equal to the old. *)(** {1 Iterators} *)valsplit:key->'at->'at*'avalueoption*'at(** [split key map] splits the map into:
- submap of [map] whose keys are smaller than [key]
- value associated to [key] (if present)
- submap of [map] whose keys are bigger than [key]
Uses the {{!unsigned_lt}unsigned order} on {!KEY.to_int}. *)valiter:(key->'avalue->unit)->'at->unit(** Iterate on each [(key,value)] pair of the map, in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valfold:(key->'avalue->'acc->'acc)->'at->'acc->'acc(** Fold on each [(key,value)] pair of the map, in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valfold_on_nonequal_inter:(key->'avalue->'avalue->'acc->'acc)->'at->'at->'acc->'acc(** [fold_on_nonequal_inter f m1 m2 acc] returns
[f key_n value1_n value2n (... (f key_1 value1_1 value2_1 acc))] where
[(key_1, value1_1, value2_1) ... (key_n, value1_n, value2_n)] are the
bindings that exist in both maps ([m1 ∩ m2]) whose values are physically different.
Calls to [f] are performed in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valfold_on_nonequal_union:(key->'avalueoption->'avalueoption->'acc->'acc)->'at->'at->'acc->'acc(** [fold_on_nonequal_union f m1 m2 acc] returns
[f key_n value1_n value2n (... (f key_1 value1_1 value2_1 acc))] where
[(key_1, value1_1, value2_1) ... (key_n, value1_n, value2_n)] are the
bindings that exists in either map ([m1 ∪ m2]) whose values are physically
different.
Calls to [f.f] are performed in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valfilter:(key->'avalue->bool)->'at->'at(** Returns the submap containing only the key->value pairs satisfying the
given predicate. [f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valfor_all:(key->'avalue->bool)->'at->bool(** Returns true if the predicate holds on all map bindings. Short-circuiting.
[f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)(** In the following, the *no_share function allows taking arguments
of different types (but cannot share subtrees of the map), while
the default functions attempt to preserve and benefit from
sharing the subtrees (using physical equality to detect
sharing). *)valmap:('avalue->'avalue)->'at->'at(** [map f m] returns a map where the [value] bound to each [key] is
replaced by [f value]. The subtrees for which the returned
value is physically the same (i.e. [f key value == value] for
all the keys in the subtree) are guaranteed to be physically
equal to the original subtree. O(n) complexity.
[f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valmap_no_share:('avalue->'bvalue)->'at->'bt(** [map_no_share f m] returns a map where the [value] bound to each
[key] is replaced by [f value]. O(n) complexity.
[f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valmapi:(key->'avalue->'avalue)->'at->'at(** [mapi f m] returns a map where the [value] bound to each [key] is
replaced by [f key value]. The subtrees for which the returned
value is physically the same (i.e. [f key value == value] for
all the keys in the subtree) are guaranteed to be physically
equal to the original subtree. O(n) complexity.
[f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valmapi_no_share:(key->'avalue->'bvalue)->'at->'bt(** [mapi_no_share f m] returns a map where the [value] bound to each
[key] is replaced by [f key value]. O(n) complexity.
[f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valfilter_map:(key->'avalue->'avalueoption)->'at->'at(** [filter_map m f] returns a map where the [value] bound to each
[key] is removed (if [f key value] returns [None]), or is
replaced by [v] ((if [f key value] returns [Some v]). The
subtrees for which the returned value is physically the same
(i.e. [f key value = Some v] with [value == v] for all the keys
in the subtree) are guaranteed to be physically equal to the
original subtree. O(n) complexity.
[f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)valfilter_map_no_share:(key->'avalue->'bvalueoption)->'at->'bt(** [filter_map m f] returns a map where the [value] bound to each
[key] is removed (if [f key value] returns [None]), or is
replaced by [v] ((if [f key value] returns [Some v]). O(n)
complexity.
[f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}. *)(** {1 Operations on pairs of maps}
See {{!BASE_MAP.functions_on_pairs}the same section for [BASE_MAP]} for
an overview of what these functions do, and an explaination of their main
differences with the equivalent functions in Stdlib's Map. *)(** {2 Comparing two maps} *)(** Equality, inclusion and test for disjoint maps. *)valreflexive_same_domain_for_all2:(key->'avalue->'avalue->bool)->'at->'at->bool(** [reflexive_same_domain_for_all2 f map1 map2] returns [true] if
[map1] and [map2] have the same keys, and [f key value1 value2]
returns true for each mapping pair of keys. We assume that [f]
is reflexive (i.e. [f key value value] returns [true]) to avoid
visiting physically equal subtrees of [map1] and [map2]. The
complexity is O(log(n)+Delta) where Delta is the number of
different keys between [map1] and [map2]. *)valnonreflexive_same_domain_for_all2:(key->'avalue->'bvalue->bool)->'at->'bt->bool(** [nonreflexive_same_domain_for_all2 f map1 map2] returns true if
map1 and map2 have the same keys, and [f key value1 value2]
returns true for each mapping pair of keys. The complexity is
O(min(|map1|,|map2|)). *)valreflexive_subset_domain_for_all2:(key->'avalue->'avalue->bool)->'at->'at->bool(** [reflexive_subset_domain_for_all2 f map1 map2] returns true if
all the keys of [map1] also are in [map2], and
[f key (find map1 key) (find map2 key)] returns [true] when both keys are present
in the map. We assume that [f] is reflexive (i.e.
[f key value value] returns true) to avoid visiting physically equal subtrees
of [map1] and [map2]. The complexity is O(log(n)*Delta) where
Delta is the number of different keys between [map1] and
[map2]. *)valreflexive_equal:('avalue->'avalue->bool)->'at->'at->bool(** [reflexive_equal f m1 m2] is true if both maps are equal, using [f] to compare values.
[f] is assumed to be reflexive (i.e. [f v v = true]).
@since v0.11.0 *)valreflexive_compare:('avalue->'avalue->int)->'at->'at->int(** [reflexive_compare f m1 m2] is an order on both maps.
[m1] and [m2] are equal (return [0]) if they have the same domain and for all bindings
[(k,v)] in [m1], [(k,v')] in [m2], we have [f v v' = 0].
[m1] is considered striclty smaller than [m2] (return a negative integer)
when the first difference (lowest key in the {{!unsigned_lt}unsigned order} of {!KEY.to_int})
is either a shared binding [(k,v)] in [m1], [(k,v')] in [m2] with [f v v' < 0],
or a binding that only occurs in [m2].
Assumes that [f v v = 0].
@since v0.11.0 *)valdisjoint:'at->'at->bool(** [disjoint a b] is [true] if and only if [a] and [b] have disjoint domains. *)valmin_binding_inter:'at->'bt->(key*'avalue*'bvalue)option(** [min_binding_inter m1 m2] is the minimal binding of the intersection.
I.E. the [(k,v1,v2)] such that
[(k,v1)] is in [m1], [(k,v2)] is in [m2], and [k] is minimal using
the {{!unsigned_lt}unsigned order} on keys.
Returns [None] if and only if the intersection is empty.
It is rougthly equivalent to calling {!unsigned_min_binding} on
{{!nonidempotent_inter_no_share}[nonindempotent_inter_no_share f m1 m2]},
but can be faster.
@since v0.11.0 *)valmax_binding_inter:'at->'bt->(key*'avalue*'bvalue)option(** [max_binding_inter m1 m2] is the same as {!min_binding_inter}, but returns
the maximum key instead of the minimum.
@since v0.11.0 *)(** {2 Combining two maps} *)(** Union, intersection, difference...
See {{!BASE_MAP.combining_maps}the same section in [BASE_MAP]} for a table showcasing
the differences between them. *)validempotent_union:(key->'avalue->'avalue->'avalue)->'at->'at->'at(** [idempotent_union f map1 map2] returns a map whose keys is the
union of the keys of [map1] and [map2]. [f] is used to combine
the values a key is mapped in both maps. We assume that [f] is
idempotent (i.e. [f key value value == value]) to avoid visiting
physically equal subtrees of [map1] and [map2], and also to
preserve physical equality of the subtreess in that case. The
complexity is O(log(n)*Delta) where Delta is the number of
different keys between [map1] and [map2].
[f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
[f] is never called on physically equal values. *)validempotent_inter:(key->'avalue->'avalue->'avalue)->'at->'at->'at(** [idempotent_inter f map1 map2] returns a map whose keys is the
intersection of the keys of [map1] and [map2]. [f] is used to combine
the values a key is mapped in both maps. We assume that [f] is
idempotent (i.e. [f key value value == value]) to avoid visiting
physically equal subtrees of [map1] and [map2], and also to
preserve physical equality of the subtrees in that case. The
complexity is O(log(n)*Delta) where Delta is the number of
different keys between [map1] and [map2].
[f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}!.
[f] is never called on physically equal values. *)valnonidempotent_inter_no_share:(key->'avalue->'bvalue->'cvalue)->'at->'bt->'ct(** [nonidempotent_inter_no_share f map1 map2] returns a map whose keys is
the intersection of the keys of [map1] and [map2]. [f] is used
to combine the values a key is mapped in both maps. [f] does not
need to be idempotent, which imply that we have to visit
physically equal subtrees of [map1] and [map2]. The complexity
is O(log(n)*min(|map1|,|map2|)).
[f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
[f] is called on every shared binding. *)validempotent_inter_filter:(key->'avalue->'avalue->'avalueoption)->'at->'at->'at(** [idempotent_inter_filter f m1 m2] is like {!idempotent_inter}
(assuming idempotence, using and preserving physically
equal subtrees), but it also removes the key->value bindings for
which [f] returns [None]. *)valslow_merge:(key->'avalueoption->'bvalueoption->'cvalueoption)->'at->'bt->'ct(** [slow_merge f m1 m2] returns a map whose keys are a subset of the
keys of [m1] and [m2]. The [f] function is used to combine
keys, similarly to the [Map.merge] function. This funcion has
to traverse all the bindings in [m1] and [m2]; its complexity is
O(|m1|+|m2|). Use one of faster functions above if you can. *)valsymmetric_difference:(key->'avalue->'avalue->'avalueoption)->'at->'at->'at(** [symmetric_difference f map1 map2] returns a map comprising of the bindings
of [map1] that aren't in [map2], and the bindings of [map2] that aren't in [map1].
Bindings that are both in [map1] and [map2], but with non-physically equal values
are passed to [f]. If [f] returns [Some v] then [v] is used as the new value,
otherwise the binding is dropped.
{b Assumes} [f] is none on equal values (i.e. [f key value value == None])
[f] is called in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
[f] is never called on physically equal values.
Complexity is [O(log n + d)] where [n] is the size of the maps, and [d] the
size of the difference.
@since v0.11.0 *)valdifference:(key->'avalue->'avalue->'avalueoption)->'at->'at->'at(** [difference f map1 map2] returns a map comprising of the bindings
of [map1] which aren't in [map2]. For keys present in both maps but with different
values, [f] is called. If it returns [Some v], then binding [k,v] is kept,
else [k] is dropped.
{b Assumes} [f] is none on equal values (i.e. [f key value value == None])
[f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
@since v0.11.0 *)(** Combination with other kinds of maps.
[Map2] must use the same {!KEY.to_int} function. *)moduleWithForeign(Map2:NODE_WITH_FINDwithtype_key=key):sigtype('b,'c)polyfilter_map_foreign={f:'a.key->('a,'b)Map2.value->'cvalueoption}[@@unboxed]valfilter_map_no_share:('b,'c)polyfilter_map_foreign->'bMap2.t->'ct(** Like [filter_map_no_share], but takes another map. *)type('value,'map2)polyinter_foreign={f:'a.'aMap2.key->'valuevalue->('a,'map2)Map2.value->'valuevalue}[@@unboxed]valnonidempotent_inter:('a,'b)polyinter_foreign->'at->'bMap2.t->'at(** Like [nonidempotent_inter], but takes another map as an argument. *)type('map1,'map2)polyupdate_multiple={f:'a.key->'map1valueoption->('a,'map2)Map2.value->'map1valueoption}[@@unboxed]valupdate_multiple_from_foreign:'bMap2.t->('a,'b)polyupdate_multiple->'at->'at(** This is equivalent to multiple calls to {!update} (but more efficient)
[update_multiple_from_foreign m_from f m_to] is the same as calling
[update k {f=fun v_to -> f.f k v_to v_from} m_to]
on all bindings [(k, v_from)] of [m_from],
i.e. [update_multiple_from_foreign m_from f m_to] calls [f.f] on every
key of [m_from], says if the corresponding value also exists in [m_to],
and adds or remove the element in [m_to] depending on the value of [f.f].
[f.f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
O(size(m_from) + size(m_to)) complexity. *)type('map1,'map2)polyupdate_multiple_inter={f:'a.key->'map1value->('a,'map2)Map2.value->'map1valueoption}[@@unboxed]valupdate_multiple_from_inter_with_foreign:'bMap2.t->('a,'b)polyupdate_multiple_inter->'at->'at(** [update_multiple_from_inter_with_foreign m_from f m_to] is the same as
{!update_multiple_from_foreign}, except that instead of updating for all
keys in [m_from], it only updates for keys that are both in [m_from] and
[m_to]. *)type('map1,'map2)polydifference=('map1,'map2)polyupdate_multiple_intervaldifference:('a,'b)polydifference->'at->'bMap2.t->'at(** [difference f map1 map2] returns the map containing the bindings of [map1]
that aren't in [map2]. For keys present in both maps but with different
values, [f.f] is called. If it returns [Some v], then binding [k,v] is kept,
else [k] is dropped.
[f.f] is called in the {{!unsigned_lt}unsigned order} of {!KEY.to_int}.
This is the same as {!MAP_WITH_VALUE.difference}, but allows the second
map to be of a different type.
@since v0.11.0 *)endvalpretty:?pp_sep:(Format.formatter->unit->unit)->(Format.formatter->key->'avalue->unit)->Format.formatter->'at->unit(** Pretty prints all bindings of the map.
[pp_sep] is called once between each binding pair and defaults to {{: https://v2.ocaml.org/api/Format.html#VALpp_print_cut}[Format.pp_print_cut]}. *)(** {1 Conversion functions} *)valto_seq:'at->(key*'avalue)Seq.t(** [to_seq m] iterates the whole map, in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)valto_rev_seq:'at->(key*'avalue)Seq.t(** [to_rev_seq m] iterates the whole map, in decreasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)valadd_seq:(key*'avalue)Seq.t->'at->'at(** [add_seq s m] adds all bindings of the sequence [s] to [m] in order. *)valof_seq:(key*'avalue)Seq.t->'at(** [of_seq s] creates a new map from the bindings of [s].
If a key is bound multiple times in [s], the latest binding is kept *)valof_list:(key*'avalue)list->'at(** [of_list l] creates a new map from the bindings of [l].
If a key is bound multiple times in [l], the latest binding is kept *)valto_list:'at->(key*'avalue)list(** [to_list m] returns the bindings of [m] as a list,
in increasing {{!unsigned_lt}unsigned order} of {!KEY.to_int} *)end(** The signature for maps with a single type for keys and values,
a ['a map] binds [key] to ['a].
Most of this interface should be shared with {{: https://ocaml.org/api/Map.S.html}[Stdlib.Map.S]}.
@canonical PatriciaTree.MAP *)moduletypeMAP=MAP_WITH_VALUEwithtype'avalue='a(** Operations added/changed in {{!hash_consed}hash-consed} maps and sets.
@canonical PatriciaTree.HASH_CONSED_OPERATIONS *)moduletypeHASH_CONSED_OPERATIONS=sigtype'at(** {1 Hash-consing specific operations} *)valto_int:'at->int(** Returns the {{!hash_consed}hash-consed} id of the map.
Unlike {!NODE_WITH_ID.to_int}, hash-consing ensures that maps
which contain the same keys (compared by {!KEY.to_int}) and values (compared
by {!HASHED_VALUE.polyeq}) will always be physically equal
and have the same identifier.
Note that when using physical equality as {!HASHED_VALUE.polyeq}, some
maps of different types [a t] and [b t] may be given the same identifier.
See the end of the documentation of {!HASHED_VALUE.polyeq} for details. *)valequal:'at->'at->bool(** Constant time equality using the {{!hash_consed}hash-consed} nodes identifiers.
This is equivalent to physical equality.
Two nodes are equal if their trees contain the same bindings,
where keys are compared by {!KEY.to_int} and values are compared by
{!HASHED_VALUE.polyeq}. *)valcompare:'at->'at->int(** Constant time comparison using the {{!hash_consed}hash-consed} node identifiers.
This order is fully arbitrary, but it is total and can be used to sort nodes.
It is based on node ids which depend on the order in which the nodes where created
(older nodes having smaller ids).
One useful property of this order is that
child nodes will always have a smaller identifier than their parents. *)end(** {1 Keys} *)(** Functor argument used to specify the key type when building the maps. *)(** The signature of homogeneous keys (non-generic, unparameterized keys).
@canonical PatriciaTree.KEY *)moduletypeKEY=sigtypet(** The type of keys.
{b It is recommended to use immutable keys.}
If keys are mutable,
any mutations to keys must preserve {!to_int}. Failing to do so will
break the patricia trees' invariants. *)(** A unique identifier for values of the type. Usually, we use a
fresh counter that is increased to give a unique id to each
object. Correctness of the operations requires that different
values in a tree correspond to different integers.
{b Must be injective, and ideally fast.}
{{: https://en.wikipedia.org/wiki/Hash_consing}hash-consing} keys is a good way to
generate such unique identifiers.
Note that since Patricia Trees use {{!unsigned_lt}unsigned order}, negative
keys are seen as bigger than positive keys.
Be wary of this when using negative keys combined with functions like
{{!BASE_MAP.unsigned_max_binding}[unsigned_max_binding]} and {{!BASE_MAP.pop_unsigned_maximum}[pop_unsigned_maximum]}. *)valto_int:t->intend(** To have heterogeneous keys, we must define a polymorphic equality
function. Like in the homogeneous case, it should have the
requirement that [(to_int a) = (to_int b) ==> polyeq a b = Eq].
@canonical PatriciaTree.cmp *)type(_,_)cmp=|Eq:('a,'a)cmp(** equality, which implies type equality. *)|Diff:('a,'b)cmp(** The signature of heterogeneous keys.
@canonical PatriciaTree.HETEROGENEOUS_KEY *)moduletypeHETEROGENEOUS_KEY=sigtype'keyt(** The type of generic/heterogeneous keys.
{b It is recommended to use immutable keys.}
If keys are mutable,
any mutations to keys must preserve {!to_int}. Failing to do so will
break the patricia trees' invariants. *)valto_int:'keyt->int(** A unique identifier for values of the type. Usually, we use a
fresh counter that is increased to give a unique id to each
object. Correctness of the operations requires that different
values in a tree correspond to different integers.
{b Must be injective, and ideally fast.}
{{: https://en.wikipedia.org/wiki/Hash_consing}hash-consing} keys is a good way to
generate such unique identifiers.
Note that since Patricia Trees use {{!unsigned_lt}unsigned order}, negative
keys are seen as bigger than positive keys.
Be wary of this when using negative keys combined with functions like
{{!BASE_MAP.unsigned_max_binding}[unsigned_max_binding]} and {{!BASE_MAP.pop_unsigned_maximum}[pop_unsigned_maximum]}. *)valpolyeq:'at->'bt->('a,'b)cmp(** Polymorphic equality function used to compare our keys.
It should satisfy [(to_int a) = (to_int b) ==> polyeq a b = Eq], and be
fast. *)end(** {1 Values} *)(** Functor argument used to specify the value type when building the maps. *)(** Module type used for specifying custom homogeneous value types in {!MakeCustomMap}.
For most purposes, use the provided {!Value} implementation.
It sets ['a t = 'a], which is the desired effect (maps can map to any value).
This is the case in {!MakeMap}.
However, for maps like {!hash_consed}, it can be useful to restrict the type
of values in order to implement [hash] and [polyeq] functions on values.
See the {!HASHED_VALUE} module type for more details.
@since v0.10.0
@canonical PatriciaTree.VALUE *)moduletypeVALUE=sigtype'at(** The type of values. A ['map map] maps [key] to ['map value].
Can be mutable if desired, unless it is being used in {!hash_consed}. *)end(** The module type of values, which can be heterogeneous.
This can be used to specify how the type of the value depends on that of the key.
If the value doesn't depend on the key type, you can use the provided default
implementations {!HomogeneousValue} and {!WrappedHomogeneousValue}.
@canonical PatriciaTree.HETEROGENEOUS_VALUE *)moduletypeHETEROGENEOUS_VALUE=sigtype('key,'map)t(** The type of values. A ['map map] maps ['key key] to [('key, 'map) value].
Can be mutable if desired, unless it is being used in {!hash_consed}. *)end(** {!VALUE} parameter for {!hash_consed}, as hash-consing requires hashing and comparing values.
This is the parameter type for homogeneous maps, used in {!MakeHashconsedMap}.
A default implementation is provided in {!HashedValue}, using
{{: https://ocaml.org/api/Hashtbl.html#VALhash}[Hashtbl.hash]}
as [hash] function and physical equality as [polyeq].
@since v0.10.0
@canonical PatriciaTree.HASHED_VALUE *)moduletypeHASHED_VALUE=sigtype'at(** The type of values for a hash-consed maps.
Unlike {!VALUE.t}, {b hash-consed values should be immutable}.
Or, if they do mutate, they must not change their {!hash} value, and
still be equal to the same values via {!polyeq} *)valhash:'mapt->int(** [hash v] should return an integer hash for the value [v].
It is used for {{!hash_consed}hash-consing}.
Hashing should be fast, avoid mapping too many values to the same integer
and compatible with {!polyeq} (equal values must have the same hash:
[polyeq v1 v2 = true ==> hash v1 = hash v2]). *)valpolyeq:'at->'bt->bool(** Polymorphic equality on values.
{b WARNING: if [polyeq a b] is true, then casting [b] to the type of [a]
(and [a] to the type of [b]) must be type-safe.} Eg. if [a : t1 t] and [b : t2 t]
yield [polyeq a b = true], then [let a' : t2 t = Obj.magic a] and
[let b' : t1 t = Obj.magic b] must be safe.
Examples of safe implementations include:
{ul
{li Having a type ['a t] which doesn't depend on ['a], in which case casting
from ['a t] to ['b t] is always safe:
{[
type _ t = foo
let cast : type a b. a t -> b t = fun x -> x
let polyeq : type a b. a t -> b t -> bool = fun x y -> x = y
]}}
{li Using a GADT type and examining its constructors to only return [true]
when the constructors are equal (or have the same type parameter):
{[
type _ t =
| T_Int : int -> int t
| T_Bool : bool -> bool t
let polyeq : type a b. a t -> b t -> bool = fun x y ->
match x, y with
| T_Int i, T_Int j -> i = j (* Here type a = b = int, we can return true *)
| T_Bool i, T_Bool j -> i && j (* same here, but with a = b = bool *)
| _ -> false (* never return true on heterogeneous cases. *)
]}}
{li Using physical equality:
{[
let polyeq a b = a == Obj.magic b
]}
While this contains an [Obj.magic], it is still type safe (OCaml just compares
the immediate values) and we can safely cast values from one type to the
other if they satisfy this (since they are already physically equal).
This is the implementation used in {!HashedValue}. Note however that
using this function can lead to {b identifiers no longer being unique across
types}. They will still be unique and behave as expected within a certain type,
but since some values of different types can physically equal, we may have
identifer clashes:
{[
# 97 == Obj.magic 'a';;
- : bool = true
]}
{[
module HMap = MakeHashconsedMap(struct
type t = int
let to_int x = x
end)(HashedValue)()
]}
{[
# let m1 = HMap.singleton 5 97;;
val m1 : int HMap.t = <abstr>
# let m2 = HMap.singleton 5 'a';;
val m2 : char HMap.t = <abstr>
# HMap.to_int m1 = HMap.to_int m2;;
- : bool = true
]}
This can cause problems if you wish to use identifiers of different map
types together:
{[
type any = Any : 'a HMap.t -> any
module MapOfMaps = MakeMap(struct
type t = any
let to_int (Any x) = HMap.to_int x
end)
]}
Using this can lead to unexpected behaviors:
in the following [m3] has cardinal 1, the [m1->"foo"] binding has been
overwritten:
{[
# let m3 = MapOfMaps.of_list [ (Any m1, "foo"); (Any m2, "bar") ]
val m3 : string MapOfMaps.t = <abstr>
# MapOfMaps.to_list m3
- : (any * string) list = [(Any <abstr>, "bar")]
]}
This issue does not happen with the two previous variants, since they
both only return true on the same types.}} *)end(** In order to build {!hash_consed}, we need to be able to hash and compare values.
This is the heterogeneous version of {!HASHED_VALUE}, used to specify a value
for heterogeneous maps (in {!MakeHashconsedHeterogeneousMap}).
A default implementation is provided in {!HeterogeneousHashedValue}, using
{{: https://ocaml.org/api/Hashtbl.html#VALhash}[Hashtbl.hash]}
as [hash] function and physical equality as [polyeq].
@since v0.10.0
@canonical PatriciaTree.HETEROGENEOUS_HASHED_VALUE *)moduletypeHETEROGENEOUS_HASHED_VALUE=sigtype('key,'map)t(** The type of values for a hash-consed maps.
Unlike {!HETEROGENEOUS_VALUE.t}, {b hash-consed values should be immutable}.
Or, if they do mutate, they must not change their {!hash} value, and
still be equal to the same values via {!polyeq} *)valhash:('key,'map)t->int(** [hash v] should return an integer hash for the value [v].
It is used for {{!hash_consed}hash-consing}.
Hashing should be fast, avoid mapping too many values to the same integer
and compatible with {!polyeq} (equal values must have the same hash:
[polyeq v1 v2 = true ==> hash v1 = hash v2]). *)valpolyeq:('key,'map_a)t->('key,'map_b)t->bool(** Polymorphic equality on values.
{b WARNING: if [polyeq a b] is true, then casting [b] to the type of [a]
(and [a] to the type of [b]) must be type-safe.} Eg. if [a : (k, t1) t] and [b : (k, t2) t]
yield [polyeq a b = true], then [let a' : (k,t2) t = Obj.magic a] and
[let b' : (k,t1) t = Obj.magic b] must be safe.
Examples of safe implementations include:
{ul
{li Having a type [('key, 'map) t] which doesn't depend on ['map] (i can depend on ['key]), in which case casting
form [('key, 'a) t] to [('key, 'b) t] is always safe:
{[
type ('k, _) t = 'k list
let cast : type a b. ('k, a) t -> ('k, b) t = fun x -> x
let polyeq : type a b. ('k, a) t -> ('k, b) t -> bool = fun x y -> x = y
]}}
{li Using a GADT type and examining its constructors to only return [true]
when the constructors are equal:
{[
type (_, _) t =
| T_Int : int -> (unit, int) t
| T_Bool : bool -> (unit, bool) t
let polyeq : type k a b. (k, a) t -> (k, b) t -> bool = fun x y ->
match x, y with
| T_Int i, T_Int j -> i = j (* Here type a = b = int, we can return true *)
| T_Bool i, T_Bool j -> i && j (* same here, but with a = b = bool *)
| _ -> false (* never return true on heterogeneous cases. *)
]}}
{li Using physical equality:
{[
let polyeq a b = a == Obj.magic b
]}
While this contains an [Obj.magic], it is still type safe (OCaml just compares
the immediate values) and we can safely cast values from one type to the
other if they satisfy this (since they are already physically equal).
This is the implementation used in {!HeterogeneousHashedValue}. Note however that
using this function can lead to {b identifiers no longer being unique across
types}. See {!HASHED_VALUE.polyeq} for more information on this.}} *)end