:- [cvs, unit_test].
:- [library(debug)].
:- debug(findsome).



%%  findsome(+MaxResults, +Skip, +Template, +Goal, -Bag)
%%
%%  Like findall/3, but lets you pick how many results to
%%  return, and how many to skip before getting those.
%%
%%  Just like all those web pages giving you the first ten
%%  results, then the next ten, etc.
%%
%%  Of course slower if you're getting them all anyway.
%%  Skipping is a little faster then building the list, and
%%  uses way less memory.   Above MaxResults, we don't
%%  do any work, so it's even faster!
%%
%%
%%  => check the swi-prolog mailing list from maybe July 2004 for
%%  a findall implementation using some new technique.  Should make
%%  this faster -- more like findall itself.
%%
findsome(Max, Skip, Template, Goal, Bag) :-
	thread_self(Self),
	atom_concat(Self, '_findsome_numfound', NumFoundFlag),
	flag(NumFoundFlag, _, 0),
	End is Skip+Max,
	findall(Template,
		(   block(wholeThing,
			  (   Goal,
			      flag(NumFoundFlag, NumFound, NumFound+1),
			      (  NumFound < Skip
			      -> fail
			      ;  (  NumFound >= End
				 -> fail(wholeThing)
				 ;  true
				 )
			      )
			  ),
			  _ExitValue
			 )
		),
		Bag
	       ).

unit_test((  findsome(4,3,X,between(1,10,X),Bag),
	     Bag = [4,5,6,7]
	  )).
unit_test((  findsome(10,3,X,between(1,10,X),Bag),
	     Bag = [4,5,6,7,8,9,10]
	  )).
unit_test((  findsome(4,0,X,between(1,10,X),Bag),
	     Bag = [1,2,3,4]
	  )).

:- thread_local(found/1).

%%   This is a wonderful function.   It's not even much slower
%%   than findall+sort for even small data sets.

setofsome(Max, Done, Template, Goal, Set) :-
	retractall(found(_)),
	thread_self(Self),
	atom_concat(Self, '_findsome_numfound', NumFoundFlag),
	flag(NumFoundFlag, _, 1),
	copy_term(pair(Template, Goal), P),
	P = pair(T2, G2),
        % loop:
	(   G2,
	    \+ found(T2),
	    assert(found(T2)),
	    flag(NumFoundFlag, N, N+1),
	    N >= Max,
	    Done=fail
	;   Done=true
	),
	!,
	findall(Template, retract(found(Template)), Set).

unit_test((  setofsome(5, Done, X, between(1,10, X), S),
	     var(X),
	     Done = fail,
	     S = [1, 2, 3, 4, 5]
	  )).

% don't count duplicates
unit_test((  setofsome(5, Done, X, member(X,[1,2,2,2,3,4,5,5,6]), S),
	     var(X),
	     Done = fail,
	     S = [1, 2, 3, 4, 5]
	  )).

% don't count duplicates IN THE TEMPLATE PART
unit_test((  setofsome(5, Done, X,
		       member(X/_,[1/a,2/a,2/b,2/c,3/a,4/a,5/a,5/a,6/a]), S),
	     var(X),
	     Done = fail,
	     S = [1, 2, 3, 4, 5]
	  )).

% stops looking for solutions when it has enough
unit_test((  setofsome(5, Done, X, between(1,1000000000, X), S),
	     var(X),
	     Done = fail,
	     S = [1, 2, 3, 4, 5]
	  )).

% stops happily when out of solutions
unit_test((  setofsome(50, Done, X, between(1,10, X), S),
	     var(X),
	     Done = true,
	     S = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
	  )).

% removes duplicates
unit_test((  setofsome(50, Done, X, ( between(1,10, X); between(1,10,X) ), S),
	     var(X),
	     Done = true,
	     S = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
	  )).


:- multifile
	user:goal_expansion/2.

user:goal_expansion(setofsome(Max, Done, T, G, Result),
		    setofsome(Max, Done, T, G2, Result)) :-
		   expand_goal(G, G2).


user:goal_expansion(findsome(Max, Skip, T, G, Result),
		    findsome(Max, Skip, T, G2, Result)) :-
		   expand_goal(G, G2).




