Source file pomap_intf.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
(*
   POMAP - Library for manipulating partially ordered maps

   Copyright (C) 2001-2006  Markus Mottl  (OEFAI)
   email: markus.mottl@gmail.com
   WWW:   http://www.ocaml.info

   This library is free software; 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; either
   version 2.1 of the License, or (at your option) any later version.

   This library 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.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*)

(** Specification of a partial order relation *)
module type PARTIAL_ORDER = sig
  type el (** Element type *)

  type ord = Unknown | Lower | Equal | Greater
  val compare : el -> el -> ord
end

(** Interface to partially ordered maps *)
module type POMAP = sig
  (** {6 Modules and types} *)

  module Store : Store_intf.STORE
  (** Store module used to store nodes of the partially ordered map. *)

  open Store

  type key (** Type of map keys *)

  type (+'a) node (** Type of nodes in the partially ordered map *)

  type (+'a) pomap (** Type of partially ordered maps *)

  (** Type of result originating from an [add_find] operation *)
  type (+'a) add_find_result =
    | Found of Ix.t * 'a node
    | Added of Ix.t * 'a node * 'a pomap


  (** {6 Map-constructors } *)

  val empty : 'a pomap
  (** The empty partially ordered map. *)

  val singleton : key -> 'a -> 'a pomap
  (** [singleton k el] @return a partially ordered map containing as only
      binding the one from [k] to [el]. *)


  (** {6 Information on maps} *)

  val is_empty : 'a pomap -> bool
  (** [is_empty pm] tests whether partially ordered map [pm] is empty. *)

  val cardinal : 'a pomap -> int
  (** [cardinal pm] @return the number of elements in [pm]. *)


  (** {6 Adding and removing} *)

  val add : key -> 'a -> 'a pomap -> 'a pomap
  (** [add k el pm] @return a partially ordered map containing the same
      bindings as [pm], plus a binding of [k] to [el]. If [k] was already
      bound in [pm], its previous binding disappears. *)

  val add_node : 'a node -> 'a pomap -> 'a pomap
  (** [add_node node pm] @return a partially ordered map containing
      the same bindings as [pm] plus a binding as represented by
      [node]. If the associated key already existed in [pm], its previous
      binding disappears. *)

  val remove : key -> 'a pomap -> 'a pomap
  (** [remove k pm] @return a map containing the same bindings as
      [pm] except for the node with key [k]. *)

  val remove_node : 'a node -> 'a pomap -> 'a pomap
  (** [remove_node node pm] @return a map containing the same bindings as
      [pm] except for the one with the key of [node]. *)

  val remove_ix : Ix.t -> 'a pomap -> 'a pomap
  (** [remove_ix ix pm] @return a map containing the same bindings as
      [pm] except for the node indexed by [ix].
      @raise Not_found if [ix] does not index any node. *)

  val take : key -> 'a pomap -> Ix.t * 'a node * 'a pomap
  (** [take k pm] @return a tuple [(ix, node, map)], where [ix] is
      the index of the [node] associated with key [k] in [pm], and [map]
      is [pm] without this element.
      @raise Not_found if there is no binding for [key]. *)

  val take_ix : Ix.t -> 'a pomap -> 'a node * 'a pomap
  (** [take_ix ix pm] @return a tuple [(n, m)], where [n] is the node
      associated with index [ix], and [m] is a map without this element.
      @raise Not_found if [ix] does not index any node. *)

  val add_find : key -> 'a -> 'a pomap -> 'a add_find_result
  (** [add_find k el pm] similar to [add], but if the binding did already
      exist, then [Found (ix, node)] will be returned to indicate the
      index and node under which key [k] is bound. Otherwise [Added
      (new_ix, new_pm)] will be returned to indicate that [k] was bound
      under new index [new_ix] in the partially ordered map [new_pm]. *)

  val add_fun : key -> 'a -> ('a -> 'a) -> 'a pomap -> 'a pomap
  (** [add_fun k el f pm] similar to [add], but if the binding already
      existed, then function [f] will be applied to the previously bound
      data. Otherwise the binding will be added as in [add]. *)


  (** {6 Scanning and searching} *)

  val mem : key -> 'a pomap -> bool
  (** [mem k pm] @return [true] if [pm] contains a binding for key [k]
      and [false] otherwise. *)

  val mem_ix : Ix.t -> 'a pomap -> bool
  (** [mem el pm] @return [true] if [pm] contains a binding for data
      element [el] and [false] otherwise. *)

  val find : key -> 'a pomap -> Ix.t * 'a node
  (** [find k pm] @return a tuple [(ix, node)], where [ix] is the index
      of key [k] and [node] its associated node in map [pm].
      @raise Not_found if no such binding exists. *)

  val find_ix : Ix.t -> 'a pomap -> 'a node
  (** [find_ix ix pm] @return the node associated with index [ix] in map [pm].
      @raise Not_found if such a node does not exist. *)

  val choose : 'a pomap -> Ix.t * 'a node
  (** [choose pm] @return a tuple [(ix, node)], where [ix] is the
      index of the [node] of some unspecified element in [pm].
      @raise Not_found if [pm] is empty. *)

  val filter : (Ix.t -> 'a node -> bool) -> 'a pomap -> 'a pomap
  (** [filter p pm] @return the map of all elements in [pm] that
      satisfy [p]. *)

  val partition : (Ix.t -> 'a node -> bool) -> 'a pomap -> 'a pomap * 'a pomap
  (** [partition p pm] @return a pair of maps [(pm1, pm2)], where
      [pm1] is the map of all the elements of [pm] that satisfy the
      predicate [p], and [pm2] is the map of all the elements of [pm]
      that do not satisfy [p]. *)


  (** {6 Iterators} *)

  val iter : ('a node -> unit) -> 'a pomap -> unit
  (** [iter f pm] applies [f] to all bound nodes in map [pm].
      The order in which the nodes are passed to [f] is unspecified. Only
      current bindings are presented to [f]: bindings hidden by more
      recent bindings are not passed to [f]. *)

  val iteri : (Ix.t -> 'a node -> unit) -> 'a pomap -> unit
  (** [iteri f pm] same as {!iter}, but function [f] also receives
      the index associated with the nodes. *)

  val map : ('a node -> 'b) -> 'a pomap -> 'b pomap
  (** [map f pm] @return a map with all nodes in [pm] mapped from
      their original value to identical nodes whose data element is in
      the codomain of [f]. The order in which nodes are passed to [f]
      is unspecified. *)

  val mapi : (Ix.t -> 'a node -> 'b) -> 'a pomap -> 'b pomap
  (** [mapi f pm] same as {!map}, but function [f] also receives
      the index associated with the nodes. *)

  val fold : ('a node -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [fold f pm a] computes [(f nN ... (f n1 a) ...)], where [n1 ... nN]
      are the nodes in map [pm]. The order in which the nodes are
      presented to [f] is unspecified. *)

  val foldi : (Ix.t -> 'a node -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [foldi f pm a] same as {!fold}, but function [f] also receives
      the index associated with the nodes. *)

  val topo_fold : ('a node -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [topo_fold f pm a] computes [(f nN ... (f n1 a) ...)], where
      [n1 ... nN] are the nodes in map [pm] sorted in ascending
      topological order. Slower than [fold]. *)

  val topo_foldi : (Ix.t -> 'a node -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [topo_foldi f pm a] same as {!topo_fold}, but function [f]
      also receives the index associated with the nodes. *)

  val topo_fold_ix : (Ix.t -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [topo_fold_ix f pm a] same as {!topo_fold}, but function [f]
      only receives the index associated with the nodes. *)

  val rev_topo_fold : ('a node -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [rev_topo_fold f pm a] computes [(f nN ... (f n1 a) ...)], where
      [n1 ... nN] are the nodes in map [pm] sorted in descending
      topological order. Slower than [fold]. *)

  val rev_topo_foldi : (Ix.t -> 'a node -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [rev_topo_foldi f pm a] same as {!rev_topo_fold}, but function [f]
      also receives the index associated with the nodes. *)

  val rev_topo_fold_ix : (Ix.t -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [rev_topo_fold_ix f pm a] same as {!rev_topo_fold}, but function [f]
      only receives the index associated with the nodes. *)

  val chain_fold : ('a node list -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [chain_fold f pm a] computes [(f cN ... (f c1 a) ...)], where
      [c1 ... cN] are the ascending chaines of nodes in map [pm]. Only
      useful for small maps, because of potentially exponential
      complexity. *)

  val chain_foldi : ((Ix.t * 'a node) list -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [chain_foldi f pm a] same as {!chain_fold}, but function [f]
      receives chains including the index associated with the nodes. *)

  val rev_chain_fold : ('a node list -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [rev_chain_fold f pm a] computes [(f cN ... (f c1 a) ...)], where
      [c1 ... cN] are the descending chaines of nodes in map [pm]. Only
      useful for small maps, because of potentially exponential
      complexity. *)

  val rev_chain_foldi :
    ((Ix.t * 'a node) list -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [rev_chain_foldi f pm a] same as {!rev_chain_fold}, but function [f]
      receives chains including the index associated with the nodes. *)


  (** {6 Set-like map-operations} *)

  val union : 'a pomap -> 'a pomap -> 'a pomap
  (** [union pm1 pm2] merges [pm1] and [pm2], preserving the
      bindings of [pm1]. *)

  val inter : 'a pomap -> 'a pomap -> 'a pomap
  (** [inter pm1 pm2] intersects [pm1] and [pm2], preserving the
      bindings of [pm1]. *)

  val diff : 'a pomap -> 'a pomap -> 'a pomap
  (** [diff pm1 pm2] removes all elements of [pm2] from [pm1]. *)


  (** {6 Node-creators and accessors} *)

  val create_node : key -> 'a -> Ix.Set.t -> Ix.Set.t -> 'a node
  (** [create_node k el sucs prds] @return a node with key [k], data
      element [el], successors [sucs] and predecessors [prds]. *)

  val get_key : 'a node -> key
  (** [get_key n] @return the key associated with node [n]. *)

  val get_el : 'a node -> 'a
  (** [get_el n] @return the data element associated with node [n]. *)

  val get_sucs : 'a node -> Ix.Set.t
  (** [get_sucs n] @return the successors associated with node [n]. *)

  val get_prds : 'a node -> Ix.Set.t
  (** [get_prds n] @return the predecessors associated with node [n]. *)

  val set_key : 'a node -> key -> 'a node
  (** [set_key n k] sets the key of node [n] to [k] and returns new node. *)

  val set_el : 'a node -> 'a -> 'a node
  (** [set_el n el] sets the data element of node [n] to [el] and
      returns new node. *)

  val set_sucs : 'a node -> Ix.Set.t -> 'a node
  (** [set_sucs n sucs] set the successors of node [n] to [sucs] and
      returns new node. *)

  val set_prds : 'a node -> Ix.Set.t -> 'a node
  (** [set_prds n prds] set the predecessors of node [n] to [prds]
      and returns new node. *)


  (** {6 Map-accessors} *)

  val get_nodes : 'a pomap -> 'a node Store.t
  (** [get_nodes pm] @return the store of nodes associated
      with partially ordered map [pm]. This store represents the
      Hasse-graph of the nodes partially ordered by their keys. *)

  val get_top : 'a pomap -> Ix.Set.t
  (** [get_top pm] @return the set of node indices of nodes that are
      greater than any other node in [pm] but themselves. *)

  val get_bot : 'a pomap -> Ix.Set.t
  (** [get_bot pm] @return the set of node indices of nodes that are
      lower than any other node in [pm] but themselves. *)


  (** {6 Operations over equivalences of data elements} *)

  val remove_eq_prds : ('a -> 'a -> bool) -> 'a pomap -> 'a pomap
  (** [remove_eq_prds eq pm] @return a map containing the same
      bindings as [pm] except for nodes whose non-empty predecessors
      all have the same data element as identified by [eq]. *)

  val fold_eq_classes :
    ('a -> 'a -> bool) -> ('a -> 'a pomap -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [fold_eq_classes eq f pm a] factorizes [pm] into maximal
      equivalence classes of partial orders: all bindings in each
      class have equivalent data elements as identified by [eq] and
      are connected in the original Hasse-diagram. This function then
      computes [(f ec_elN ecN ... (f ec_el1 ec1 a))], where [ec1 ... ecN]
      are the mentioned equivalence classes in unspecified order, and
      [ec_el1 ... ec_elN] are their respective common data elements. *)

  val fold_split_eq_classes :
    ('a -> 'a -> bool) -> ('a -> 'a pomap -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [fold_split_eq_classes eq f pm a] same as {!fold_eq_classes},
      but the equivalence classes are split further so that no element
      of other classes would fit between its bottom and top elements.
      It is unspecified how non-conflicting elements are assigned to
      upper or lower classes! *)

  val preorder_eq_classes : ('a -> 'a -> bool) -> 'a pomap -> 'a pomap list
  (** [preorder_eq_classes eq pm] @return a preordered list of
      equivalence classes, the latter being defined as in
      [fold_split_eq_classes]. *)

  val topo_fold_reduced :
    ('a -> 'a -> bool) -> ('a node -> 'b -> 'b) -> 'a pomap -> 'b -> 'b
  (** [topo_fold_reduced eq f pm a] computes [(f nN ... (f n1 a) ...)],
      where [n1 ... nN] are those nodes in map [pm] sorted in ascending
      topological order, whose data element is equivalent as defined by
      [eq] to the one of lower elements if there are no intermediate
      elements that violate this equivalence. *)


  (** {6 Unsafe operations - USE WITH CAUTION!} *)

  val unsafe_update : 'a pomap -> Ix.t -> 'a node -> 'a pomap
  (** [unsafe_update pm ix node] updates the node associated with node
      index [ix] in map [pm] with [node]. The Hasse-diagram associated
      with the partially ordered map [pm] may become inconsistent if
      the new node violates the partial order structure. This can lead
      to unpredictable results with other functions! *)

  val unsafe_set_nodes : 'a pomap -> 'a node Store.t -> 'a pomap
  (** [unsafe_set_nodes pm s] updates the node store associated with map
      [pm] with [s].  This assumes that [s] stores a consistent
      Hasse-diagram of nodes. *)

  val unsafe_set_top : 'a pomap -> Ix.Set.t -> 'a pomap
  (** [unsafe_set_top pm set] updates the index of top nodes in
      map [pm] with [set].  This assumes that the nodes referenced by
      the node indices in [set] do not violate the properties of the
      Hasse-diagram of [pm]. *)

  val unsafe_set_bot : 'a pomap -> Ix.Set.t -> 'a pomap
  (** [unsafe_set_bot pm set] updates the index of bottom nodes
      in map [pm] with [set].  This assumes that the nodes referenced
      by the node indices in [set] do not violate the properties of the
      Hasse-diagram of [pm]. *)
end