+
+ # Filter elements to return only the smallest ones
+ #
+ # ~~~
+ # var s = new POSet[String]
+ # s.add_edge("B", "A")
+ # s.add_edge("C", "A")
+ # s.add_edge("D", "B")
+ # s.add_edge("D", "C")
+ # assert s.select_smallest(["A", "B"]) == ["B"]
+ # assert s.select_smallest(["A", "B", "C"]) == ["B", "C"]
+ # assert s.select_smallest(["B", "C", "D"]) == ["D"]
+ # ~~~
+ fun select_smallest(elements: Collection[E]): Array[E]
+ do
+ var res = new Array[E]
+ for e in elements do
+ for f in elements do
+ if e == f then continue
+ if has_edge(f, e) then continue label
+ end
+ res.add(e)
+ end label
+ return res
+ end
+
+ # Filter elements to return only the greatest ones
+ #
+ # ~~~
+ # var s = new POSet[String]
+ # s.add_edge("B", "A")
+ # s.add_edge("C", "A")
+ # s.add_edge("D", "B")
+ # s.add_edge("D", "C")
+ # assert s.select_greatest(["A", "B"]) == ["A"]
+ # assert s.select_greatest(["A", "B", "C"]) == ["A"]
+ # assert s.select_greatest(["B", "C", "D"]) == ["B", "C"]
+ # ~~~
+ fun select_greatest(elements: Collection[E]): Array[E]
+ do
+ var res = new Array[E]
+ for e in elements do
+ for f in elements do
+ if e == f then continue
+ if has_edge(e, f) then continue label
+ end
+ res.add(e)
+ end label
+ return res
+ end
+
+ # Sort a sorted array of poset elements using linearization order
+ # ~~~~
+ # var pos = new POSet[String]
+ # pos.add_chain(["A", "B", "C", "D", "E"])
+ # pos.add_chain(["A", "X", "C", "Y", "E"])
+ # var a = pos.linearize(["X", "C", "E", "A", "D"])
+ # assert a == ["E", "D", "C", "X", "A"]
+ # ~~~~
+ fun linearize(elements: Collection[E]): Array[E] do
+ var lin = elements.to_a
+ sort(lin)
+ return lin
+ end
+
+ redef fun clone do return sub(self)
+
+ # Return an induced sub-poset
+ #
+ # The elements of the result are those given in argument.
+ #
+ # ~~~
+ # var pos = new POSet[String]
+ # pos.add_chain(["A", "B", "C", "D", "E"])
+ # pos.add_chain(["A", "X", "C", "Y", "E"])
+ #
+ # var pos2 = pos.sub(["A", "B", "D", "Y", "E"])
+ # assert pos2.has_exactly(["A", "B", "D", "Y", "E"])
+ # ~~~
+ #
+ # The full relationship is preserved between the provided elements.
+ #
+ # ~~~
+ # for e1 in pos2 do for e2 in pos2 do
+ # assert pos2.has_edge(e1, e2) == pos.has_edge(e1, e2)
+ # end
+ # ~~~
+ #
+ # Not that by definition, the direct relationship is the transitive
+ # reduction of the full reduction. Thus, the direct relationship of the
+ # sub-poset may not be included in the direct relationship of self because an
+ # indirect edge becomes a direct one if all the intermediates elements
+ # are absent in the sub-poset.
+ #
+ # ~~~
+ # assert pos.has_direct_edge("B", "D") == false
+ # assert pos2.has_direct_edge("B", "D") == true
+ #
+ # assert pos2["B"].direct_greaters.has_exactly(["D", "Y"])
+ # ~~~
+ #
+ # If the `elements` contains all then the result is a clone of self.
+ #
+ # ~~~
+ # var pos3 = pos.sub(pos)
+ # assert pos3 == pos
+ # assert pos3 == pos.clone
+ # ~~~
+ fun sub(elements: Collection[E]): POSet[E]
+ do
+ var res = new POSet[E]
+ for e in self do
+ if not elements.has(e) then continue
+ res.add_node(e)
+ end
+ for e in res do
+ for f in self[e].greaters do
+ if not elements.has(f) then continue
+ res.add_edge(e, f)
+ end
+ end
+ return res
+ end
+
+ # Two posets are equal if they contain the same elements and edges.
+ #
+ # ~~~
+ # var pos1 = new POSet[String]
+ # pos1.add_chain(["A", "B", "C", "D", "E"])
+ # pos1.add_chain(["A", "X", "C", "Y", "E"])
+ #
+ # var pos2 = new POSet[Object]
+ # pos2.add_edge("Y", "E")
+ # pos2.add_chain(["A", "X", "C", "D", "E"])
+ # pos2.add_chain(["A", "B", "C", "Y"])
+ #
+ # assert pos1 == pos2
+ #
+ # pos1.add_edge("D", "Y")
+ # assert pos1 != pos2
+ #
+ # pos2.add_edge("D", "Y")
+ # assert pos1 == pos2
+ #
+ # pos1.add_node("Z")
+ # assert pos1 != pos2
+ # ~~~
+ redef fun ==(other) do
+ if not other isa POSet[nullable Object] then return false
+ if not self.elements.keys.has_exactly(other.elements.keys) then return false
+ for e, ee in elements do
+ if ee.direct_greaters != other[e].direct_greaters then return false
+ end
+ assert hash == other.hash
+ return true
+ end
+
+ redef fun hash
+ do
+ var res = 0
+ for e, ee in elements do
+ if e == null then continue
+ res += e.hash
+ res += ee.direct_greaters.length
+ end
+ return res
+ end
+
+ redef fun core_serialize_to(serializer)
+ do
+ # Optimize written data because this structure has duplicated data
+ # For example, serializing the class hierarchy of a simple program where E is String
+ # result is before: 200k, after: 56k.
+ serializer.serialize_attribute("elements", elements)
+ end
+
+ redef init from_deserializer(deserializer)
+ do
+ deserializer.notify_of_creation self
+ var elements = deserializer.deserialize_attribute("elements")
+ if elements isa HashMap[E, POSetElement[E]] then
+ self.elements = elements
+ end
+ end