[net.sources] Prolog library: bagutl.pl

pereira@sri-unix.UUCP (08/22/83)

/*	BAGUTL.PL		Bag Utilities
	R.A.O'Keefe.		Updated: 10 Sept 81

    A bag B is a function from a set dom(B) to the non-negative integers.
For the purposes of this module, a bag is constructed from two functions:
	
	bag		- creates an empty bag
	bag(E, M, B)	- extends the bag B with a new (NB!) element E
			  which occurs with multiplicity M, and which
			  precedes all elements of B in Prolog's order.

A bag is represented by a Prolog term mirroring its construction.  There
is one snag with this: what are we to make of
	bag(f(a,Y), 1, bag(f(X,b), 1, bag))	?
As a term it has two distinct elements, but f(a,b) will be reported as
occurring in it twice.  But according to the definition above,
	bag(f(a,b), 1, bag(f(a,b), 1, bag))
is not the representation of any bag, that bag is represented by
	bag(f(a,b), 2, bag)
alone.  We are apparently stuck with a scheme which is only guaranteed
to work for ground terms.

    The reason for insisting on the order is to make union and 
intersection linear in the sizes of their arguments.

*/

:- public is_bag/1.
:-  mode  is_bag(+), is_bag(+, +), posint(+).

is_bag(bag(E, M, B)) :-
	posint(M),
	is_bag(B, E).
is_bag(bag).

	is_bag(bag(E, M, B), P) :-
		E @> P,
		posint(M),
		is_bag(B, E).
	is_bag(bag, P).

	posint(M) :-
		integer(M),
		M > 0.


:- public portray_bag/1.
:-  mode  portray_bag(+), pbag(+, +, +), pbag(+, +).

portray_bag(bag(E,M,B)) :-
	write('[% '), pbag(E, M, B), write(' %]').
portray_bag(bag) :-
	write('[% '),		     write(' %]').

	pbag(E, M, B) :-
		var(B), !,
		pbag(E, M), write(' | '), write(B).
	pbag(E, M, bag(F, N, B)) :- !,
		pbag(E, M), write(', '),  pbag(F, N, B).
	pbag(E, M, bag) :- !,
		pbag(E, M).
	pbag(E, M, B) :-
		pbag(E, M), write(' | '), write(B).

		pbag(E, M) :-
			print(E), write(':'), write(M).


%   If bags are to be as useful as lists, we should provide mapping
%   predicates similar to those for lists.  Hence
%	checkbag(Pred, Bag)		- applies Pred(Element, Count)
%	mapbag(Pred, BagIn, BagOut)	- applies Pred(Element, Answer)
%   Note that mapbag does NOT give the Count to Pred, but preserves it.
%   It wouldn't be hard to apply Pred to four arguments if it wants them.


:- public checkbag/2,	  mapbag/3.
:-  mode  checkbag(+, +), mapbag(+, +, -), mapbaglist(+, +, -).

checkbag(Pred, bag(E, M, B)) :-
	apply(Pred, [E, M]),
	checkbag(Pred, B).
checkbag(Pred, bag).


mapbag(Pred, BagIn, BagOut) :-
	mapbaglist(Pred, BagIn, Listed),
	keysort(Listed, Sorted),
	bagform(Sorted, BagOut).

	mapbaglist(Pred, bag(E, M, B), [R-M|L]) :-
		apply(Pred, [E, R]),
		mapbaglist(Pred, B, L).
	mapbaglist(Pred, bag, []).


:- public bag_to_list/2.
:-  mode  bag_to_list(+, -), bag_to_list(+, +, -, -).

 bag_to_list(bag(E, M, B), R) :-
	bag_to_list(M, E, L, R),
	bag_to_list(B, L).
bag_to_list(bag, []).

	bag_to_list(0, E, L, L) :- !.
	bag_to_list(M, E, L, [E|R]) :-
		N is M-1,
		bag_to_list(N, E, L, R).


:- public list_to_bag/2.
:-  mode  list_to_bag(+, -), bagform(+, -), bagform(?, +, -, +, -).
:-  mode  addkeys(+, -).	%  hack to circumvent 'sort' nastiness

list_to_bag(L, B) :-
	addkeys(L, K),
	keysort(K, S),
	bagform(S, B).

	addkeys([Head|Tail], [Head-1|Rest]) :-
		addkeys(Tail, Rest).
	addkeys([], []).

	bagform([], bag) :- !.
	bagform(List, bag(E, M, B)) :-
		bagform(E, List, Rest, 0, M), !,
		bagform(Rest, B).

		bagform(Head, [Head-N|Tail], Rest, K, M) :-!,
			L is K+N,
			bagform(Head, Tail, Rest, L, M).
		bagform(Head, Rest, Rest, M, M).


:- public bag_to_set/2.
:-  mode  bag_to_set(+, -).

bag_to_set(bag(E, M, B), [E|S]) :-
	bag_to_set(B, S).
bag_to_set(bag, []).


/*  There are two versions of the routines member, bagmax, and bagmin.
    The slow versions, which are commented out, try to allow for the
    possibility that distinct elements in the bag might unify, while
    the faster routines assume that all elements are ground terms.

:- public member/3.
:-  mode  member(?, -, +), member(+, +, +, -).

member(E, M, bag(E, K, B)) :-
	member(B, E, K, M).
member(E, M, bag(_, _, B)) :-
	member(E, M, B).

	member(bag(E, L, B), E, K, M) :- !,
		N is K+L,
		member(B, E, N, M).
	member(bag(_, _, B), E, K, M) :-
		member(B, E, K, M).
	member(bag,	     E, M, M).

:- public bagmax/2,	bagmin/2.
:-  mode  bagmax(+, ?), bagmin(+, ?).
%  These routines are correct, but Oh, so costly!

bagmax(B, E) :-
	member(E, M, B),
	\+ (member(F, N, B), N > M).

bagmin(B, E) :-
	member(E, M, B),
	\+ (member(F, N, B), N < M).

*//*	The faster versions follow    */

:- public member/3.
:-  mode  member(?, ?, +).

member(Element, Multiplicity, bag(Element, Multiplicity, _)).
member(Element, Multiplicity, bag(_, _, Bag)) :-
	member(Element, Multiplicity, Bag).


:- public bagmax/2,	bagmin/2.
:-  mode  bagmax(+, -), bagmin(+, -), bag_scan(+, +, +, -, +).

bagmax(bag(E, M, B), Emax) :-
	bag_scan(B, E, M, Emax, >).

bagmin(bag(E, M, B), Emin) :-
	bag_scan(B, E, M, Emin, <).

	bag_scan(bag(Eb,Mb,B), Ei, Mi, Eo, C) :-
		compare(C, Mb, Mi), !,
		bag_scan(B, Eb, Mb, Eo, C).
	bag_scan(bag(Eb,Mb,B), Ei, Mi, Eo, C) :-
		bag_scan(B, Ei, Mi, Eo, C).
/*	bag_scan(bag(Eb,Mb,B), Ei, Mi, Eo, C) :-
		bag_scan(B, Eb, Mb, Eo, C).	%  for all extrema
*/	bag_scan(bag,	       Ei, Mi, Ei, C).


:- public length/3.
:-  mode  length(+, -, -), length(+, +, -, +, -).


length(B, BL, SL) :-
	length(B, 0, BL, 0, SL).

	length(bag(_,M,B), BA, BL, SA, SL) :-
		BB is BA+M, SB is SA+1,
		length(B, BB, BL, SB, SL).
	length(bag,	   BL, BL, SL, SL).


%  sub_bag, if it existed, could be used two ways: to test whether one bag
%  is a sub_bag of another, or to generate all the sub_bags.  The two uses
%  need different implementations.

:- public make_sub_bag/2.
:-  mode  make_sub_bag(+, -), countdown(+, -).

make_sub_bag(bag(E, M, B), bag(E, N, C)) :-
	countdown(M, N),
	make_sub_bag(B, C).
make_sub_bag(bag(E, M, B), C) :-
	make_sub_bag(B, C).
make_sub_bag(bag, bag).

	countdown(M, M).
	countdown(M, N) :-
		M > 1, K is M-1,
		countdown(K, N).


:- public test_sub_bag/2.
:-  mode  test_sub_bag(+, +), test_sub_bag(+, +, +, +, +, +, +).

test_sub_bag(bag(E1, M1, B1), bag(E2, M2, B2)) :-
	compare(C, E1, E2),
	test_sub_bag(C, E1, M1, B1, E2, M2, B2).
test_sub_bag(bag, Bag).

	test_sub_bag(>, E1, M1, B1, E2, M2, B2) :-
		test_sub_bag(bag(E1, M1, B1), B2).
	test_sub_bag(=, E1, M1, B1, E1, M2, B2) :-
		M1 =< M2,
		test_sub_bag(B1, B2).

:- public bag_union/3.
:-  mode  bag_union(+, +, -), bag_union(+, +, +, +, +, +, +, -).

bag_union(bag(E1, M1, B1), bag(E2, M2, B2), B3) :-
	compare(C, E1, E2), !,
	bag_union(C, E1, M1, B1, E2, M2, B2, B3).
bag_union(bag, Bag, Bag) :- !.
bag_union(Bag, bag, Bag).

	bag_union(<, E1, M1, B1, E2, M2, B2, bag(E1, M1, B3)) :-
		bag_union(B1, bag(E2, M2, B2), B3).
	bag_union(>, E1, M1, B1, E2, M2, B2, bag(E2, M2, B3)) :-
		bag_union(bag(E1, M1, B1), B2, B3).
	bag_union(=, E1, M1, B1, E1, M2, B2, bag(E1, M3, B3)) :-
		M3 is M1+M2,
		bag_union(B1, B2, B3).


:- public bag_inter/3.
:-  mode  bag_inter(+, +, -), bag_inter(+, +, +, +, +, +, +, -).

bag_inter(bag(E1, M1, B1), bag(E2, M2, B2), B3) :-
	compare(C, E1, E2), !,
	bag_inter(C, E1, M1, B1, E2, M2, B2, B3).
bag_inter(B1, B2, bag).

	bag_inter(<, E1, M1, B1, E2, M2, B2, B3) :-
		bag_inter(B1, bag(E2, M2, B2), B3).
	bag_inter(>, E1, M1, B1, E2, M2, B2, B3) :-
		bag_inter(bag(E1, M1, B1), B2, B3).
	bag_inter(=, E1, M1, B1, E1, M2, B2, bag(E1, M3, B3)) :-
		(   M1 < M2, M3 = M1  ;  M3 = M2   ), !,
		bag_inter(B1, B2, B3).

/*  Sorted list of Bag Utilities:

bag_inter(+Bag1, +Bag2, -Inter)
bag_to_list(+Bag, -List)
bag_to_set(+Bag, -SetList)
bag_union(+Bag1, +Bag2, -Union)
bagmax(+Bag, ?Elem)
bagmin(+Bag, ?Elem)
checkbag(+Pred, +Bag)
is_bag(+Bag)
length(+Bag, -Total, -Distinct)
list_to_bag(+List, -Bag)
make_sub_bag(+Bag, -SubBag)
mapbag(+Pred, +BagIn, -BagOut)
member(?Elem, -Count, +Bag)
portray_bag(+Bag)
test_sub_bag(+SubBag, +Bag)

End of List	*/