% ---------------------------------------------------------------------- % System: ECLiPSe Constraint Logic Programming System % Version: $Id: ordset.pl,v 1.3 2010/03/19 05:53:50 jschimpf Exp $ % ---------------------------------------------------------------------- % % ECLiPSe PROLOG LIBRARY MODULE % % $Id: ordset.pl,v 1.3 2010/03/19 05:53:50 jschimpf Exp $ % % IDENTIFICATION: ordset.pl % % AUTHOR: Joachim Schimpf, IC-Parc, Imperial College, London % % DESCRIPTION: % % This is a drop-in replacement for R.A.O'Keefe's ordset library. % The predicates have all been rewritten for optimal indexing % and garbage avoidance when used within ECLiPSe. I have kept % the original interface and comments. % 2005: added functionality of recent proposals and variants. % % File : ORDSET.PL % Author : R.A.O'Keefe % Updated: 22 May 1983 % Purpose: Ordered set manipulation utilities % In this module, sets are represented by ordered lists with no % duplicates. Thus {c,r,a,f,t} would be [a,c,f,r,t]. The ordering % is defined by the @< family of term comparison predicates, which % is the ordering used by sort/2 and setof/3. % The benefit of the ordered representation is that the elementary % set operations can be done in time proportional to the Sum of the % argument sizes rather than their Product. Some of the unordered % set routines, such as member/2, length/2,, select/3 can be used % unchanged. The main difficulty with the ordered representation is % remembering to use it! :- module(ordset). :- comment(categories, ["Data Structures"]). :- comment(summary, "Ordered set manipulation utilities"). :- comment(author, "R.A.O'Keefe and Joachim Schimpf"). :- comment(copyright, 'This file is in the public domain'). :- comment(date, "$Date: 2010/03/19 05:53:50 $"). :- comment(desc, html("

\ In this module, sets are represented by ordered lists with no duplicates. Thus the set {c,r,a,f,t} would be [a,c,f,r,t]. The ordering is defined by the @< family of term comparison predicates, which is the ordering used by sort/2 and setof/3.

The benefit of the ordered representation is that the elementary set operations can be done in time proportional to the Sum of the argument sizes rather than their Product. Some of the unordered set routines, such as member/2, length/2, select/3 can be used unchanged.

The implementation allows nonground set elements. The only problem with this is that a set can lose its set property as a result of variable bindings: unifications can create duplicates or change the element's position in the term order. The set can be repaired by applying list_to_ord_set/2 again, i.e. re-sorting it.

Note that the predicates of this library do no error checking. When called with the wrong argument types or modes, the result is undefined.

")). % Compatibility: % P O'Keefe's public domain library 1983 % Q Quintus % S SICStus % K O'Keefe's standard library draft 2005 % (http://www.cs.otago.ac.nz/staffpriv/ok/pllib.htm) :- export % Signature Compatibility is_ordset/1, % List -> - Q S K list_to_ord_set/2, % List -> Set P Q S K ord_add_element/3, % Set x Elem -> Set - Q S - ord_compare/3, % Set x Set -> Rel - - - K ord_del_element/3, % Set x Elem -> Set - Q S - ord_disjoint/2, % Set x Set -> P Q S K ord_disjoint_union/3, % Set x Set -> Set - - - K ord_insert/3, % Set x Elem -> Set P - - - ord_intersect/2, % Set x Set -> P Q S K ord_intersect/3, % Set x Set -> Set P - - - ord_intersection/2, % Sets -> Set - Q S K ord_intersection/3, % Set x Set -> Set - Q S K % ord_intersection/4, % Set x Set -> Set x Set - - S - ord_intersection/5, % Set x Set -> Set x Set x Set - - - - ord_memberchk/2, % Elem x Set -> - - - K ord_nonmember/2, % Elem x Set -> - - - K ord_proper_subset/2, % Set x Set -> - - - K ord_proper_superset/2, % Set x Set -> - - - K ord_selectchk/3, % Elem x Set -> Set - - - K ord_seteq/2, % Set x Set -> P Q S - % ord_setproduct/3, % Set x Set -> List - P S - ord_subset/2, % Set x Set -> P Q S K ord_subtract/3, % Set x Set -> Set P Q S K ord_superset/2, % Set x Set -> - - - K ord_symdiff/3, % Set x Set -> Set P Q S K ord_union/2, % Sets -> Set - Q S K ord_union/3, % Set x Set -> Set P Q S K ord_union/4. % Set x Set -> Set x Set - Q S K :- comment(list_to_ord_set/2, [ amode:(list_to_ord_set(+,-) is det), args:["List":"A list of terms", "Set":"A set or variable"], summary:"Converts a list to a set", see_also:[sort/2], desc:html("\ Succeeds when Set is the ordered representation of the set represented by the unordered representation List. The only reason for giving it a name at all is that you may not have realised that sort/2 could be used this way." )]). :- mode list_to_ord_set(+, ?). list_to_ord_set(List, Set) :- sort(0, <, List, Set). :- comment(is_ordset/1, [ amode:(is_ordset(?) is semidet), args:["Term":"Any Term"], summary:"Checks whether term is an ordered set in the sense of lib(ordset)", see_also:[@< /2], desc:html("\ Checks whether term is an ordered set in the sense of lib(ordset), i.e. a proper, duplicate-free list with elements in increasing order. ")]). is_ordset([]) ?- true. is_ordset([X|Xs]) ?- is_ordset(X, Xs). is_ordset(_, []) ?- true. is_ordset(X1, [X2|Xs]) ?- X1 @< X2, is_ordset(X2, Xs). :- comment(ord_memberchk/2, [ amode:(ord_memberchk(?,+) is semidet), args:["Term":"Any Term", "Set":"A set"], summary:"Checks whether Term is a member of Set", see_also:[member/2,ord_nonmember/2,ord_selectchk/3], fail_if:"Term is not a member of Set" ]). :- mode ord_memberchk(?,+). ord_memberchk(X, [Y|Ys]) :- compare(R, X, Y), ( R == (>), ord_memberchk(X, Ys) ; R == (=) % a la O'Keefe, but different from memberchk/2! ). :- comment(ord_del_element/3, [ amode:(ord_del_element(+,?,-) is det), args:["Set":"A set", "Term":"Any Term", "Remainder":"A variable or set"], summary:"Remainder is the set Set without the element Term", see_also:[ord_selectchk/3], desc:html("\ Remainder is the set Set without the element Term. If Term is not in Set, Remainder is identical to Set.") ]). :- mode ord_del_element(+,?,?). ord_del_element([], _, []). ord_del_element(YYs, X, Rs) :- YYs = [Y|Ys], compare(R, X, Y), ( R == (>) -> Rs = [Y|Rs0], ord_del_element(Ys, X, Rs0) ; R == (=) -> Rs = Ys ; Rs = YYs ). :- comment(ord_selectchk/3, [ amode:(ord_selectchk(?,+,-) is semidet), args:["Term":"Any Term", "Set":"A set", "Remainder":"A variable or set"], summary:"Set contains Term, and Remainder is the set Set without Term", fail_if:"Term is not a member of Set", see_also:[ord_del_element/3, delete/3], desc:html("\ If Set contains Term, Remainder is the set Set without the Term. Otherwise the predicate fails.") ]). :- mode ord_selectchk(?,+,?). ord_selectchk(X, [Y|Ys], Rs) :- compare(R, X, Y), ( R == (>), Rs = [Y|Rs0], ord_selectchk(X, Ys, Rs0) ; R == (=), Rs = Ys ). :- comment(ord_nonmember/2, [ amode:(ord_nonmember(?,+) is semidet), args:["Term":"Any Term", "Set":"A set"], summary:"Term is not a member of Set", see_also:[ord_memberchk/2], fail_if:"Term is a member of Set" ]). :- mode ord_nonmember(?,+). ord_nonmember(_X, []). ord_nonmember(X, [Y|Ys]) :- compare(R, X, Y), ( R == (>), ord_nonmember(X, Ys) ; R == (<) % a la O'Keefe, but different from nonmember/2! ). :- comment(ord_compare/3, [ amode:(ord_compare(-,+,+) is semidet), args:["Rel":"A variable or an atom", "Set1":"A set", "Set2":"A set"], summary:"Rel is the ordering relationship between Set1 and Set2", see_also:[@< /2, ord_seteq/2, ord_proper_subset/2, ord_proper_superset/2], fail_if:"Fails if the sets are not comparable", desc:html("\ Rel is the ordering relationship between Set1 and Set2. Rel is one of the atoms =, > or <
	=    The sets are identical (in the sense of ==/2)
	>    Set1 is a proper superset of Set2
	<    Set1 is a proper subset of Set2
	
Otherwise the predicate fails.") ]). :- mode ord_compare(?,+,+). ord_compare(R, Xs, Ys) :- ord_compare1(R, Xs, Ys, =). :- mode ord_compare1(?, +, +, +). ord_compare1(R, [], [], RSofar) :- !, R = RSofar. ord_compare1(R, [], [_|_], RSofar) :- !, RSofar \== (>), R = (<). ord_compare1(R, [_|_], [], RSofar) :- !, RSofar \== (<), R = (>). ord_compare1(R, XXs, YYs, RSofar) :- YYs = [Y|Ys], XXs = [X|Xs], compare(R1, X, Y), ( R1 == (=) -> ord_compare1(R, Xs, Ys, RSofar) ; R1 == (<) -> RSofar \== (<), ord_compare1(R, Xs, YYs, >) ; RSofar \== (>), ord_compare1(R, XXs, Ys, <) ). :- comment(ord_disjoint/2, [ amode:(ord_disjoint(+,+) is semidet), args:["Set1":"A set", "Set2":"A set"], summary:"Checks whether two sets are disjoint", see_also:[ord_intersect/2], desc:html("\ Succeeds when the two ordered sets have no element in common. ")]). :- mode ord_disjoint(+,+). ord_disjoint([], _). ord_disjoint(S1, S2) :- S1=[_|_], ord_disjoint1(S1, S2). :- mode ord_disjoint1(+, +). ord_disjoint1(_, []). ord_disjoint1(EL1, EL2) :- EL2 = [E2|L2], EL1 = [E1|L1], compare(R, E1, E2), ( R == (<), ord_disjoint(L1, EL2) ; R == (>), ord_disjoint(EL1, L2) ). :- comment(ord_insert/3, [ amode:(ord_insert(+,+,-) is det), args:["Set1":"A set", "Element":"A term", "Set2":"A set or variable"], summary:"Adds an element to a set", see_also:[ord_add_element/3], desc:html("\ Set2 is the set resulting from adding Element to Set1. It should give exactly the same result as merge(Set1, [Element], Set2). This is a synonym of ord_add_element/3. ")]). :- comment(ord_add_element/3, [ amode:(ord_add_element(+,+,-) is det), args:["Set1":"A set", "Element":"A term", "Set2":"A set or variable"], summary:"Adds an element to a set", see_also:[ord_insert/3], desc:html("\ Set2 is the set resulting from adding Element to Set1. It should give exactly the same result as merge(Set1, [Element], Set2). This is a synonym of ord_insert/3. ")]). :- mode ord_add_element(+, ?, ?). ord_add_element(S1, E, S2) :- ord_insert(S1, E, S2). :- mode ord_insert(+, ?, ?). ord_insert([], E, [E]). ord_insert(EL1, E, L) :- EL1 = [E1|L1], compare(R, E1, E), ( R == (<) -> L = [E1|L0], ord_insert(L1, E, L0) ; R == (=) -> L = EL1 ; L = [E|EL1] ). :- comment(ord_intersect/2, [ amode:(ord_intersect(+,+) is semidet), args:["Set1":"A set", "Set2":"A set"], summary:"Checks whether two sets have a non-empty intersection", see_also:[ord_disjoint/2], desc:html("\ Succeeds when the two ordered sets have at least one element in common. Note that the test is == rather than = . ")]). :- mode ord_intersect(+, +). ord_intersect(EL1, EL2) :- EL2 = [E2|L2], EL1 = [E1|L1], compare(R, E1, E2), ( R == (=) -> true ; R == (<) -> ord_intersect(L1, EL2) ; ord_intersect(EL1, L2) ). :- comment(ord_intersect/3, [ amode:(ord_intersect(+,+,-) is det), args:["Set1":"A set", "Set2":"A set", "Intersection":"A set"], summary:"Computes the intersection of two sets", see_also:[ord_intersection/3], desc:html("

\ Succeeds when Intersection is the intersection of Set1 and Set2, provided that Set1 and Set2 are ordered sets.

The use of ord_intersection/3 is preferred.

")]). :- comment(ord_intersection/3, [ amode:(ord_intersection(+,+,-) is det), args:["Set1":"A set", "Set2":"A set", "Intersection":"A set"], summary:"Computes the intersection of two sets", see_also:[ord_intersect/2, ord_intersection/5], desc:html("\ Succeeds when Intersection is the intersection of Set1 and Set2, provided that Set1 and Set2 are ordered sets. ")]). :- mode ord_intersect(+, +, ?). ord_intersect(S1, S2, S) :- ord_intersection(S1, S2, S). :- mode ord_intersection(+, +, ?). ord_intersection([], _, []). ord_intersection(S1, S2, L) :- S1=[_|_], ord_intersection1(S1, S2, L). :- mode ord_intersection1(+, +, ?). ord_intersection1(_, [], []). ord_intersection1(EL1, EL2, L) :- EL2 = [E2|L2], EL1 = [E1|L1], compare(R, E1, E2), ( R == (=) -> L = [E1|L0], ord_intersection(L1, L2, L0) ; R == (<) -> ord_intersection(L1, EL2, L) ; ord_intersection1(EL1, L2, L) ). :- comment(ord_intersection/5, [ amode:(ord_intersection(+,+,-,-,-) is det), args:["Set1":"A set", "Set2":"A set", "Intersection":"A variable or set", "Only1":"A variable or set", "Only2":"A variable or set" ], summary:"Computes the intersection and the differences of two sets", see_also:[ord_intersection/3, ord_subtract/3, ord_symdiff/3], desc:html("\ Succeeds when Intersection is the intersection of the ordered sets Set1 and Set2, Only1 is the set of elements that are only in Set1, and Only2 is the set of elements that are only in Set2. ")]). % ord_intersection(+Xs, +Ys, -CommonElements, -XRemains, -YRemains) % computes intersection and remainders of two ordered lists. :- mode ord_intersection(+,+,?,?,?). ord_intersection([], Ys, [], [], Ys). ord_intersection(Xs, Ys, Cs, Ls, Rs) :- Xs=[_|_], ord_intersection1(Xs, Ys, Cs, Ls, Rs). ord_intersection1(Xs, [], [], Xs, []). ord_intersection1(XXs, YYs, Cs, Ls, Rs) :- YYs = [Y|Ys], XXs = [X|Xs], compare(R, X, Y), ( R == (=) -> Cs = [X|Cs0], ord_intersection(Xs, Ys, Cs0, Ls, Rs) ; R == (<) -> Ls = [X|Ls0], ord_intersection(Xs, YYs, Cs, Ls0, Rs) ; Rs = [Y|Rs0], ord_intersection1(XXs, Ys, Cs, Ls, Rs0) ). :- comment(ord_seteq/2, [ amode:(ord_seteq(+,+) is semidet), args:["Set1":"A set", "Set2":"A set"], summary:"Compares two sets for identity", see_also:[== /2, ord_compare/3], desc:html("\ Succeeds when the two arguments represent the same set. Since they are assumed to be ordered representations, they must be identical. ")]). :- mode ord_seteq(+, +). ord_seteq(Set1, Set2) :- Set1 == Set2. :- comment(ord_subset/2, [ amode:(ord_subset(+,+) is semidet), args:["Set1":"A set", "Set2":"A set"], summary:"Checks whether Set1 is a subset of Set2", see_also:[ord_proper_subset/2, subset/2, ord_compare/3], desc:html("\ Succeeds when every element of the ordered set Set1 appears in the ordered set Set2. ")]). :- mode ord_subset(+, +). ord_subset([], _). ord_subset(EL1, EL2) :- EL1 = [E1|L1], EL2 = [E2|L2], compare(R, E1, E2), ( R == (=), ord_subset(L1, L2) ; R == (>), ord_subset(EL1, L2) ). :- comment(ord_superset/2, [ amode:(ord_superset(+,+) is semidet), args:["Set1":"A set", "Set2":"A set"], summary:"Checks whether Set1 is a superset of Set2", see_also:[ord_proper_superset/2, subset/2, ord_compare/3], desc:html("\ Succeeds when every element of the ordered set Set2 appears in the ordered set Set1. ")]). :- mode ord_superset(+, +). ord_superset(S1, S2) :- ord_subset(S2, S1). :- comment(ord_proper_subset/2, [ amode:(ord_proper_subset(+,+) is semidet), args:["Set1":"A set", "Set2":"A set"], summary:"Checks whether Set1 is a proper subset of Set2", see_also:[ord_subset/2, ord_compare/3], desc:html("\ Succeeds when every element of the ordered set Set1 appears in the ordered set Set2, and Set2 has at least on element that does not occur in Set1. ")]). :- mode ord_proper_subset(+, +). ord_proper_subset([], [_|_]). ord_proper_subset(EL1, EL2) :- EL1 = [E1|L1], EL2 = [E2|L2], compare(R, E1, E2), ( R == (=), ord_proper_subset(L1, L2) ; R == (>), ord_subset(EL1, L2) ). :- comment(ord_proper_superset/2, [ amode:(ord_proper_superset(+,+) is semidet), args:["Set1":"A set", "Set2":"A set"], summary:"Checks whether Set1 is a proper superset of Set2", see_also:[ord_superset/2, ord_compare/3], desc:html("\ Succeeds when every element of the ordered set Set2 appears in the ordered set Set1, and Set1 has at least on element that does not occur in Set2. ")]). :- mode ord_proper_superset(+, +). ord_proper_superset(S1, S2) :- ord_proper_subset(S2, S1). :- comment(ord_subtract/3, [ amode:(ord_subtract(+,+,-) is det), args:["Set1":"A set", "Set2":"A set", "Difference":"A set or variable"], summary:"Subtracts Set2 from Set1", see_also:[ord_symdiff/3], desc:html("\ Succeeds when Difference contains all and only the elements of Set1 which are not also in Set2. ")]). :- mode ord_subtract(+, +, ?). ord_subtract([], _, []). ord_subtract(S1, S2, L) :- S1=[_|_], ord_subtract1(S1, S2, L). :- mode ord_subtract1(+, +, ?). ord_subtract1(L, [], L). ord_subtract1(EL1, EL2, L) :- EL2 = [E2|L2], EL1 = [E1|L1], compare(R, E1, E2), ( R == (=) -> ord_subtract(L1, L2, L) ; R == (<) -> L = [E1|L0], ord_subtract(L1, EL2, L0) ; ord_subtract(EL1, L2, L) ). :- comment(ord_symdiff/3, [ amode:(ord_symdiff(+,+,-) is det), args:["Set1":"A set", "Set2":"A set", "Difference":"A set or variable"], summary:"Computes the symmetric difference of Set1 and Set2", see_also:[ord_subtract/3], desc:html("\ Succeeds when Difference is the symmetric difference of Set1 and Set2. These are the elements that occur either only in Set1 or only in Set2, but not both. ")]). :- mode ord_symdiff(+, +, ?). ord_symdiff([], L, L). ord_symdiff(S1, S2, L) :- S1=[_|_], ord_symdiff1(S1, S2, L). :- mode ord_symdiff1(+, +, ?). ord_symdiff1(L, [], L). ord_symdiff1(EL1, EL2, L) :- EL2 = [E2|L2], EL1 = [E1|L1], compare(R, E1, E2), ( R == (=) -> ord_symdiff(L1, L2, L) ; R == (<) -> L = [E1|L0], ord_symdiff(L1, EL2, L0) ; L = [E2|L0], ord_symdiff(EL1, L2, L0) ). :- comment(ord_union/3, [ amode:(ord_union(+,+,-) is det), args:["Set1":"A set", "Set2":"A set", "Union":"A set or variable"], summary:"Computes the union of Set1 and Set2", see_also:[ord_disjoint_union/3, ord_union/4, ord_union/2], desc:html("\ Succeeds when Union is the union of Set1 and Set2. Note that when something occurs in both sets, we want to retain only one copy. ")]). :- mode ord_union(+, +, ?). ord_union(Set1, Set2, Union) :- merge(0, <, Set1, Set2, Union). :- comment(ord_union/4, [ amode:(ord_union(+,+,-,-) is det), args:["Set1":"A set", "Set2":"A set", "Union":"A set or variable", "New":"A set or variable"], summary:"Computes the union and difference of Set2 and Set1", see_also:[ord_disjoint_union/3, ord_union/3, ord_union/2], desc:html("\ Succeeds when Union is the union of Set1 and Set2, and New is the set of elements that are in Set2 but not in Set1. This is useful in order to know which elements were newly added when incrementally merging sets. ")]). :- mode ord_union(+, +, ?, ?). ord_union([], Ys, Ys, Ys). ord_union(Xs, Ys, Us, Rs) :- Xs=[_|_], ord_union1(Xs, Ys, Us, Rs). ord_union1(Xs, [], Xs, []). ord_union1(XXs, YYs, Us, Rs) :- YYs = [Y|Ys], XXs = [X|Xs], compare(R, X, Y), ( R == (=) -> Us = [X|Us0], ord_union(Xs, Ys, Us0, Rs) ; R == (<) -> Us = [X|Us0], ord_union(Xs, YYs, Us0, Rs) ; Us = [Y|Us0], Rs = [Y|Rs0], ord_union1(XXs, Ys, Us0, Rs0) ). :- comment(ord_disjoint_union/3, [ amode:(ord_disjoint_union(+,+,-) is semidet), args:["Set1":"A set", "Set2":"A set", "Union":"A set or variable"], summary:"Computes the union of Set1 and Set2 when they are disjoint", see_also:[ord_union/4, ord_union/2], fail_if:"Fails if the sets are not disjoint", desc:html("\ Succeeds when Union is the union of disjoint sets Set1 and Set2. Fails if Set1 and Set2 are not disjoint, i.e. if they have a non-empty intersection. ")]). :- mode ord_disjoint_union(+, +, ?). ord_disjoint_union([], Ys, Ys). ord_disjoint_union(Xs, Ys, Us) :- Xs=[_|_], ord_disjoint_union1(Xs, Ys, Us). ord_disjoint_union1(Xs, [], Xs). ord_disjoint_union1(XXs, YYs, Us) :- YYs = [Y|Ys], XXs = [X|Xs], compare(R, X, Y), % fail if not disjoint ( R == (<), Us = [X|Us0], ord_disjoint_union(Xs, YYs, Us0) ; R == (>), Us = [Y|Us0], ord_disjoint_union1(XXs, Ys, Us0) ). :- comment(ord_union/2, [ amode:(ord_union(+,-) is det), args:["Sets":"A list of sets", "Union":"A set or variable"], summary:"Computes the union of all sets in Sets", see_also:[ord_union/3, ord_intersection/2], desc:html("\ Succeeds when Union is the union of all sets in the list Sets. ")]). :- mode ord_union(+,?). ord_union([], []). ord_union([Set1|Sets], Union) :- append(Sets, Tail, Head), ord_union_queue(Set1, Head, Tail, Union). ord_union_queue(Set, Head, _Tail, Union) :- var(Head), !, Union = Set. ord_union_queue(Set1, [Set2|Sets], [Set12|Tail], Union) :- ord_union(Set1, Set2, Set12), Sets = [Set3|Head], ord_union_queue(Set3, Head, Tail, Union). :- comment(ord_intersection/2, [ amode:(ord_intersection(+,-) is det), args:["Sets":"A list of sets", "Intersection":"A set or variable"], summary:"Computes the intersection of all sets in Sets", see_also:[ord_intersection/3, ord_union/2], desc:html("\ Succeeds when Intersection is the intersection of all sets in the list Sets. ")]). :- mode ord_intersection(+,?). ord_intersection([], []). ord_intersection([Set|Sets], Common) :- ord_intersection_multi(Sets, Set, Common). ord_intersection_multi([], Common, Common). ord_intersection_multi([Set|Sets], Common0, Common) :- ( Common0 == [] -> Common = Common0 % finish early ; ord_intersection(Common0, Set, Common1), ord_intersection_multi(Sets, Common1, Common) ).