View source with raw comments or as raw
   1/*  Part of SWI-Prolog
   2
   3    Author:        Jan Wielemaker
   4    E-mail:        J.Wielemaker@vu.nl
   5    WWW:           http://www.swi-prolog.org
   6    Copyright (c)  2008-2016, University of Amsterdam
   7                              VU University Amsterdam
   8    All rights reserved.
   9
  10    Redistribution and use in source and binary forms, with or without
  11    modification, are permitted provided that the following conditions
  12    are met:
  13
  14    1. Redistributions of source code must retain the above copyright
  15       notice, this list of conditions and the following disclaimer.
  16
  17    2. Redistributions in binary form must reproduce the above copyright
  18       notice, this list of conditions and the following disclaimer in
  19       the documentation and/or other materials provided with the
  20       distribution.
  21
  22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33    POSSIBILITY OF SUCH DAMAGE.
  34*/
  35
  36:- module(aggregate,
  37          [ foreach/2,                  % :Generator, :Goal
  38            aggregate/3,                % +Templ, :Goal, -Result
  39            aggregate/4,                % +Templ, +Discrim, :Goal, -Result
  40            aggregate_all/3,            % +Templ, :Goal, -Result
  41            aggregate_all/4,            % +Templ, +Discrim, :Goal, -Result
  42            free_variables/4            % :Generator, :Template, +Vars0, -Vars
  43          ]).
  44:- use_module(library(ordsets)).
  45:- use_module(library(pairs)).
  46:- use_module(library(error)).
  47:- use_module(library(lists)).
  48:- use_module(library(apply)).
  49
  50:- meta_predicate
  51    foreach(0,0),
  52    aggregate(?,^,-),
  53    aggregate(?,?,^,-),
  54    aggregate_all(?,0,-),
  55    aggregate_all(?,?,0,-).
  56
  57/** <module> Aggregation operators on backtrackable predicates
  58
  59This library provides aggregating operators  over   the  solutions  of a
  60predicate. The operations are a generalisation   of the bagof/3, setof/3
  61and findall/3 built-in predicates. The   defined  aggregation operations
  62are counting, computing the sum, minimum,   maximum,  a bag of solutions
  63and a set of solutions. We first   give  a simple example, computing the
  64country with the smallest area:
  65
  66==
  67smallest_country(Name, Area) :-
  68        aggregate(min(A, N), country(N, A), min(Area, Name)).
  69==
  70
  71There are four aggregation predicates (aggregate/3, aggregate/4, aggregate_all/3 and aggregate/4), distinguished on two properties.
  72
  73    $ aggregate vs. aggregate_all :
  74    The aggregate predicates use setof/3 (aggregate/4) or bagof/3
  75    (aggregate/3), dealing with existential qualified variables
  76    (Var^Goal) and providing multiple solutions for the remaining free
  77    variables in Goal. The aggregate_all/3 predicate uses findall/3,
  78    implicitly qualifying all free variables and providing exactly one
  79    solution, while aggregate_all/4 uses sort/2 over solutions that
  80    Discriminator (see below) generated using findall/3.
  81
  82    $ The Discriminator argument :
  83    The versions with 4 arguments deduplicate redundant solutions of
  84    Goal. Solutions for which both the template variables and
  85    Discriminator are identical will be treated as one solution. For
  86    example, if we wish to compute the total population of all
  87    countries, and for some reason =|country(belgium, 11000000)|= may
  88    succeed twice, we can use the following to avoid counting the
  89    population of Belgium twice:
  90
  91    ==
  92        aggregate(sum(P), Name, country(Name, P), Total)
  93    ==
  94
  95All aggregation predicates support  the   following  operators  below in
  96Template. In addition, they allow for  an arbitrary named compound term,
  97where each of the arguments is a term  from the list below. For example,
  98the term r(min(X), max(X)) computes both the minimum and maximum binding
  99for X.
 100
 101        * count
 102        Count number of solutions.  Same as sum(1).
 103        * sum(Expr)
 104        Sum of Expr for all solutions.
 105        * min(Expr)
 106        Minimum of Expr for all solutions.
 107        * min(Expr, Witness)
 108        A term min(Min, Witness), where Min is the minimal version
 109        of Expr over all solutions, and Witness is any other template
 110        applied to solutions that produced Min.  If multiple
 111        solutions provide the same minimum, Witness corresponds to
 112        the first solution.
 113        * max(Expr)
 114        Maximum of Expr for all solutions.
 115        * max(Expr, Witness)
 116        As min(Expr, Witness), but producing the maximum result.
 117        * set(X)
 118        An ordered set with all solutions for X.
 119        * bag(X)
 120        A list of all solutions for X.
 121
 122*Acknowledgements*
 123
 124_|The development of this library was sponsored by SecuritEase,
 125  http://www.securitease.com
 126|_
 127
 128@compat Quintus, SICStus 4. The forall/2 is a SWI-Prolog built-in and
 129        term_variables/3 is a SWI-Prolog built-in with
 130        *|different semantics|*.
 131@tbd    Analysing the aggregation template and compiling a predicate
 132        for the list aggregation can be done at compile time.
 133@tbd    aggregate_all/3 can be rewritten to run in constant space using
 134        non-backtrackable assignment on a term.
 135*/
 136
 137                 /*******************************
 138                 *           AGGREGATE          *
 139                 *******************************/
 140
 141%!  aggregate(+Template, :Goal, -Result) is nondet.
 142%
 143%   Aggregate bindings in Goal according to Template.  The aggregate/3
 144%   version performs bagof/3 on Goal.
 145
 146aggregate(Template, Goal0, Result) :-
 147    template_to_pattern(bag, Template, Pattern, Goal0, Goal, Aggregate),
 148    bagof(Pattern, Goal, List),
 149    aggregate_list(Aggregate, List, Result).
 150
 151%!  aggregate(+Template, +Discriminator, :Goal, -Result) is nondet.
 152%
 153%   Aggregate bindings in Goal according to Template.  The aggregate/4
 154%   version performs setof/3 on Goal.
 155
 156aggregate(Template, Discriminator, Goal0, Result) :-
 157    template_to_pattern(bag, Template, Pattern, Goal0, Goal, Aggregate),
 158    setof(Discriminator-Pattern, Goal, Pairs),
 159    pairs_values(Pairs, List),
 160    aggregate_list(Aggregate, List, Result).
 161
 162%!  aggregate_all(+Template, :Goal, -Result) is semidet.
 163%
 164%   Aggregate  bindings  in  Goal   according    to   Template.  The
 165%   aggregate_all/3 version performs findall/3 on   Goal.  Note that
 166%   this predicate fails if Template contains one or more of min(X),
 167%   max(X),  min(X,Witness)  or  max(X,Witness)  and   Goal  has  no
 168%   solutions, i.e., the minumum and  maximum   of  an  empty set is
 169%   undefined.
 170
 171aggregate_all(Var, _, _) :-
 172    var(Var),
 173    !,
 174    instantiation_error(Var).
 175aggregate_all(count, Goal, Count) :-
 176    !,
 177    aggregate_all(sum(1), Goal, Count).
 178aggregate_all(sum(X), Goal, Sum) :-
 179    !,
 180    State = state(0),
 181    (  call(Goal),
 182           arg(1, State, S0),
 183           S is S0 + X,
 184           nb_setarg(1, State, S),
 185           fail
 186    ;  arg(1, State, Sum)
 187    ).
 188aggregate_all(max(X), Goal, Max) :-
 189    !,
 190    State = state(X),
 191    (  call(Goal),
 192           arg(1, State, M0),
 193           M is max(M0,X),
 194           nb_setarg(1, State, M),
 195           fail
 196    ;  arg(1, State, Max),
 197           nonvar(Max)
 198    ).
 199aggregate_all(min(X), Goal, Min) :-
 200    !,
 201    State = state(X),
 202    (  call(Goal),
 203           arg(1, State, M0),
 204           M is min(M0,X),
 205           nb_setarg(1, State, M),
 206           fail
 207    ;  arg(1, State, Min),
 208           nonvar(Min)
 209    ).
 210aggregate_all(max(X,W), Goal, max(Max,Witness)) :-
 211    !,
 212    State = state(false, _Max, _Witness),
 213    (  call(Goal),
 214           (   State = state(true, Max0, _)
 215           ->  X > Max0,
 216               nb_setarg(2, State, X),
 217               nb_setarg(3, State, W)
 218           ;   number(X)
 219           ->  nb_setarg(1, State, true),
 220               nb_setarg(2, State, X),
 221               nb_setarg(3, State, W)
 222           ;   type_error(number, X)
 223           ),
 224           fail
 225    ;  State = state(true, Max, Witness)
 226    ).
 227aggregate_all(min(X,W), Goal, min(Min,Witness)) :-
 228    !,
 229    State = state(false, _Min, _Witness),
 230    (  call(Goal),
 231           (   State = state(true, Min0, _)
 232           ->  X < Min0,
 233               nb_setarg(2, State, X),
 234               nb_setarg(3, State, W)
 235           ;   number(X)
 236           ->  nb_setarg(1, State, true),
 237               nb_setarg(2, State, X),
 238               nb_setarg(3, State, W)
 239           ;   type_error(number, X)
 240           ),
 241           fail
 242    ;  State = state(true, Min, Witness)
 243    ).
 244aggregate_all(Template, Goal0, Result) :-
 245    template_to_pattern(all, Template, Pattern, Goal0, Goal, Aggregate),
 246    findall(Pattern, Goal, List),
 247    aggregate_list(Aggregate, List, Result).
 248
 249%!  aggregate_all(+Template, +Discriminator, :Goal, -Result) is semidet.
 250%
 251%   Aggregate  bindings  in  Goal   according    to   Template.  The
 252%   aggregate_all/4 version performs findall/3 followed by sort/2 on
 253%   Goal. See aggregate_all/3 to understand   why this predicate can
 254%   fail.
 255
 256aggregate_all(Template, Discriminator, Goal0, Result) :-
 257    template_to_pattern(all, Template, Pattern, Goal0, Goal, Aggregate),
 258    findall(Discriminator-Pattern, Goal, Pairs0),
 259    sort(Pairs0, Pairs),
 260    pairs_values(Pairs, List),
 261    aggregate_list(Aggregate, List, Result).
 262
 263template_to_pattern(All, Template, Pattern, Goal0, Goal, Aggregate) :-
 264    template_to_pattern(Template, Pattern, Post, Vars, Aggregate),
 265    existential_vars(Goal0, Goal1, AllVars, Vars),
 266    clean_body((Goal1, Post), Goal2),
 267    (   All == bag
 268    ->  add_existential_vars(AllVars, Goal2, Goal)
 269    ;   Goal = Goal2
 270    ).
 271
 272existential_vars(Var, Var) -->
 273    { var(Var) },
 274    !.
 275existential_vars(Var^G0, G) -->
 276    !,
 277    [Var],
 278    existential_vars(G0, G).
 279existential_vars(M:G0, M:G) -->
 280    !,
 281    existential_vars(G0, G).
 282existential_vars(G, G) -->
 283    [].
 284
 285add_existential_vars([], G, G).
 286add_existential_vars([H|T], G0, H^G1) :-
 287    add_existential_vars(T, G0, G1).
 288
 289
 290%!  clean_body(+Goal0, -Goal) is det.
 291%
 292%   Remove redundant =true= from Goal0.
 293
 294clean_body((Goal0,Goal1), Goal) :-
 295    !,
 296    clean_body(Goal0, GoalA),
 297    clean_body(Goal1, GoalB),
 298    (   GoalA == true
 299    ->  Goal = GoalB
 300    ;   GoalB == true
 301    ->  Goal = GoalA
 302    ;   Goal = (GoalA,GoalB)
 303    ).
 304clean_body(Goal, Goal).
 305
 306
 307%!  template_to_pattern(+Template, -Pattern, -Post, -Vars, -Aggregate)
 308%
 309%   Determine which parts of the goal we must remember in the
 310%   findall/3 pattern.
 311%
 312%   @param Post is a body-term that evaluates expressions to reduce
 313%               storage requirements.
 314%   @param Vars is a list of intermediate variables that must be
 315%               added to the existential variables for bagof/3.
 316%   @param Aggregate defines the aggregation operation to execute.
 317
 318template_to_pattern(Term, Pattern, Goal, Vars, Aggregate) :-
 319    templ_to_pattern(Term, Pattern, Goal, Vars, Aggregate),
 320    !.
 321template_to_pattern(Term, Pattern, Goal, Vars, term(MinNeeded, Functor, AggregateArgs)) :-
 322    compound(Term),
 323    !,
 324    Term =.. [Functor|Args0],
 325    templates_to_patterns(Args0, Args, Goal, Vars, AggregateArgs),
 326    needs_one(AggregateArgs, MinNeeded),
 327    Pattern =.. [Functor|Args].
 328template_to_pattern(Term, _, _, _, _) :-
 329    invalid_template(Term).
 330
 331templ_to_pattern(sum(X),           X,         true,    [],   sum) :- var(X), !.
 332templ_to_pattern(sum(X0),          X,         X is X0, [X0], sum) :- !.
 333templ_to_pattern(count,            1,         true,    [],   count) :- !.
 334templ_to_pattern(min(X),           X,         true,    [],   min) :- var(X), !.
 335templ_to_pattern(min(X0),          X,         X is X0, [X0], min) :- !.
 336templ_to_pattern(min(X0, Witness), X-Witness, X is X0, [X0], min_witness) :- !.
 337templ_to_pattern(max(X0),          X,         X is X0, [X0], max) :- !.
 338templ_to_pattern(max(X0, Witness), X-Witness, X is X0, [X0], max_witness) :- !.
 339templ_to_pattern(set(X),           X,         true,    [],   set) :- !.
 340templ_to_pattern(bag(X),           X,         true,    [],   bag) :- !.
 341
 342templates_to_patterns([], [], true, [], []).
 343templates_to_patterns([H0], [H], G, Vars, [A]) :-
 344    !,
 345    sub_template_to_pattern(H0, H, G, Vars, A).
 346templates_to_patterns([H0|T0], [H|T], (G0,G), Vars, [A0|A]) :-
 347    sub_template_to_pattern(H0, H, G0, V0, A0),
 348    append(V0, RV, Vars),
 349    templates_to_patterns(T0, T, G, RV, A).
 350
 351sub_template_to_pattern(Term, Pattern, Goal, Vars, Aggregate) :-
 352    templ_to_pattern(Term, Pattern, Goal, Vars, Aggregate),
 353    !.
 354sub_template_to_pattern(Term, _, _, _, _) :-
 355    invalid_template(Term).
 356
 357invalid_template(Term) :-
 358    callable(Term),
 359    !,
 360    domain_error(aggregate_template, Term).
 361invalid_template(Term) :-
 362    type_error(aggregate_template, Term).
 363
 364%!  needs_one(+Ops, -OneOrZero)
 365%
 366%   If one of the operations in Ops needs at least one answer,
 367%   unify OneOrZero to 1.  Else 0.
 368
 369needs_one(Ops, 1) :-
 370    member(Op, Ops),
 371    needs_one(Op),
 372    !.
 373needs_one(_, 0).
 374
 375needs_one(min).
 376needs_one(min_witness).
 377needs_one(max).
 378needs_one(max_witness).
 379
 380%!  aggregate_list(+Op, +List, -Answer) is semidet.
 381%
 382%   Aggregate the answer  from  the   list  produced  by  findall/3,
 383%   bagof/3 or setof/3. The latter  two   cases  deal  with compound
 384%   answers.
 385%
 386%   @tbd    Compile code for incremental state update, which we will use
 387%           for aggregate_all/3 as well.  We should be using goal_expansion
 388%           to generate these clauses.
 389
 390aggregate_list(bag, List0, List) :-
 391    !,
 392    List = List0.
 393aggregate_list(set, List, Set) :-
 394    !,
 395    sort(List, Set).
 396aggregate_list(sum, List, Sum) :-
 397    sum_list(List, Sum).
 398aggregate_list(count, List, Count) :-
 399    length(List, Count).
 400aggregate_list(max, List, Sum) :-
 401    max_list(List, Sum).
 402aggregate_list(max_witness, List, max(Max, Witness)) :-
 403    max_pair(List, Max, Witness).
 404aggregate_list(min, List, Sum) :-
 405    min_list(List, Sum).
 406aggregate_list(min_witness, List, min(Min, Witness)) :-
 407    min_pair(List, Min, Witness).
 408aggregate_list(term(0, Functor, Ops), List, Result) :-
 409    !,
 410    maplist(state0, Ops, StateArgs, FinishArgs),
 411    State0 =.. [Functor|StateArgs],
 412    aggregate_term_list(List, Ops, State0, Result0),
 413    finish_result(Ops, FinishArgs, Result0, Result).
 414aggregate_list(term(1, Functor, Ops), [H|List], Result) :-
 415    H =.. [Functor|Args],
 416    maplist(state1, Ops, Args, StateArgs, FinishArgs),
 417    State0 =.. [Functor|StateArgs],
 418    aggregate_term_list(List, Ops, State0, Result0),
 419    finish_result(Ops, FinishArgs, Result0, Result).
 420
 421aggregate_term_list([], _, State, State).
 422aggregate_term_list([H|T], Ops, State0, State) :-
 423    step_term(Ops, H, State0, State1),
 424    aggregate_term_list(T, Ops, State1, State).
 425
 426
 427%!  min_pair(+Pairs, -Key, -Value) is det.
 428%!  max_pair(+Pairs, -Key, -Value) is det.
 429%
 430%   True if Key-Value has the  smallest/largest   key  in  Pairs. If
 431%   multiple pairs share the smallest/largest key, the first pair is
 432%   returned.
 433
 434min_pair([M0-W0|T], M, W) :-
 435    min_pair(T, M0, W0, M, W).
 436
 437min_pair([], M, W, M, W).
 438min_pair([M0-W0|T], M1, W1, M, W) :-
 439    (   M0 < M1
 440    ->  min_pair(T, M0, W0, M, W)
 441    ;   min_pair(T, M1, W1, M, W)
 442    ).
 443
 444max_pair([M0-W0|T], M, W) :-
 445    max_pair(T, M0, W0, M, W).
 446
 447max_pair([], M, W, M, W).
 448max_pair([M0-W0|T], M1, W1, M, W) :-
 449    (   M0 > M1
 450    ->  max_pair(T, M0, W0, M, W)
 451    ;   max_pair(T, M1, W1, M, W)
 452    ).
 453
 454%!  step(+AggregateAction, +New, +State0, -State1).
 455
 456step(bag,   X, [X|L], L).
 457step(set,   X, [X|L], L).
 458step(count, _, X0, X1) :-
 459    succ(X0, X1).
 460step(sum,   X, X0, X1) :-
 461    X1 is X0+X.
 462step(max,   X, X0, X1) :-
 463    X1 is max(X0, X).
 464step(min,   X, X0, X1) :-
 465    X1 is min(X0, X).
 466step(max_witness, X-W, X0-W0, X1-W1) :-
 467    (   X > X0
 468    ->  X1 = X, W1 = W
 469    ;   X1 = X0, W1 = W0
 470    ).
 471step(min_witness, X-W, X0-W0, X1-W1) :-
 472    (   X < X0
 473    ->  X1 = X, W1 = W
 474    ;   X1 = X0, W1 = W0
 475    ).
 476step(term(Ops), Row, Row0, Row1) :-
 477    step_term(Ops, Row, Row0, Row1).
 478
 479step_term(Ops, Row, Row0, Row1) :-
 480    functor(Row, Name, Arity),
 481    functor(Row1, Name, Arity),
 482    step_list(Ops, 1, Row, Row0, Row1).
 483
 484step_list([], _, _, _, _).
 485step_list([Op|OpT], Arg, Row, Row0, Row1) :-
 486    arg(Arg, Row, X),
 487    arg(Arg, Row0, X0),
 488    arg(Arg, Row1, X1),
 489    step(Op, X, X0, X1),
 490    succ(Arg, Arg1),
 491    step_list(OpT, Arg1, Row, Row0, Row1).
 492
 493finish_result(Ops, Finish, R0, R) :-
 494    functor(R0, Functor, Arity),
 495    functor(R, Functor, Arity),
 496    finish_result(Ops, Finish, 1, R0, R).
 497
 498finish_result([], _, _, _, _).
 499finish_result([Op|OpT], [F|FT], I, R0, R) :-
 500    arg(I, R0, A0),
 501    arg(I, R, A),
 502    finish_result1(Op, F, A0, A),
 503    succ(I, I2),
 504    finish_result(OpT, FT, I2, R0, R).
 505
 506finish_result1(bag, Bag0, [], Bag) :-
 507    !,
 508    Bag = Bag0.
 509finish_result1(set, Bag,  [], Set) :-
 510    !,
 511    sort(Bag, Set).
 512finish_result1(max_witness, _, M-W, R) :-
 513    !,
 514    R = max(M,W).
 515finish_result1(min_witness, _, M-W, R) :-
 516    !,
 517    R = min(M,W).
 518finish_result1(_, _, A, A).
 519
 520%!  state0(+Op, -State, -Finish)
 521
 522state0(bag,   L, L).
 523state0(set,   L, L).
 524state0(count, 0, _).
 525state0(sum,   0, _).
 526
 527%!  state1(+Op, +First, -State, -Finish)
 528
 529state1(bag, X, L, [X|L]) :- !.
 530state1(set, X, L, [X|L]) :- !.
 531state1(_,   X, X, _).
 532
 533
 534                 /*******************************
 535                 *             FOREACH          *
 536                 *******************************/
 537
 538%!  foreach(:Generator, :Goal)
 539%
 540%   True if conjunction of results is   true. Unlike forall/2, which
 541%   runs a failure-driven loop that proves Goal for each solution of
 542%   Generator, foreach/2 creates a conjunction.   Each member of the
 543%   conjunction is a copy of  Goal,   where  the variables it shares
 544%   with Generator are filled with the values from the corresponding
 545%   solution.
 546%
 547%   The implementation executes forall/2 if   Goal  does not contain
 548%   any variables that are not shared with Generator.
 549%
 550%   Here is an example:
 551%
 552%   ==
 553%   ?- foreach(between(1,4,X), dif(X,Y)), Y = 5.
 554%   Y = 5.
 555%   ?- foreach(between(1,4,X), dif(X,Y)), Y = 3.
 556%   false.
 557%   ==
 558%
 559%   @bug    Goal is copied repeatedly, which may cause problems if
 560%           attributed variables are involved.
 561
 562foreach(Generator, Goal) :-
 563    term_variables(Generator, GenVars0), sort(GenVars0, GenVars),
 564    term_variables(Goal, GoalVars0), sort(GoalVars0, GoalVars),
 565    ord_subtract(GoalVars, GenVars, SharedGoalVars),
 566    (   SharedGoalVars == []
 567    ->  \+ (Generator, \+Goal)      % = forall(Generator, Goal)
 568    ;   ord_intersection(GenVars, GoalVars, SharedVars),
 569        Templ =.. [v|SharedVars],
 570        SharedTempl =.. [v|SharedGoalVars],
 571        findall(Templ, Generator, List),
 572        prove_list(List, Templ, SharedTempl, Goal)
 573    ).
 574
 575prove_list([], _, _, _).
 576prove_list([H|T], Templ, SharedTempl, Goal) :-
 577    copy_term(Templ+SharedTempl+Goal,
 578              H+SharedTempl+Copy),
 579    Copy,
 580    prove_list(T, Templ, SharedTempl, Goal).
 581
 582
 583%!  free_variables(:Generator, +Template, +VarList0, -VarList) is det.
 584%
 585%   Find free variables in bagof/setof template.  In order to handle
 586%   variables  properly,  we  have  to   find  all  the  universally
 587%   quantified variables in the  Generator.   All  variables  as yet
 588%   unbound are universally quantified, unless
 589%
 590%       1. they occur in the template
 591%       2. they are bound by X^P, setof/3, or bagof/3
 592%
 593%   free_variables(Generator, Template, OldList, NewList) finds this
 594%   set using OldList as an accumulator.
 595%
 596%   @author Richard O'Keefe
 597%   @author Jan Wielemaker (made some SWI-Prolog enhancements)
 598%   @license Public domain (from DEC10 library).
 599%   @tbd Distinguish between control-structures and data terms.
 600%   @tbd Exploit our built-in term_variables/2 at some places?
 601
 602free_variables(Term, Bound, VarList, [Term|VarList]) :-
 603    var(Term),
 604    term_is_free_of(Bound, Term),
 605    list_is_free_of(VarList, Term),
 606    !.
 607free_variables(Term, _Bound, VarList, VarList) :-
 608    var(Term),
 609    !.
 610free_variables(Term, Bound, OldList, NewList) :-
 611    explicit_binding(Term, Bound, NewTerm, NewBound),
 612    !,
 613    free_variables(NewTerm, NewBound, OldList, NewList).
 614free_variables(Term, Bound, OldList, NewList) :-
 615    functor(Term, _, N),
 616    free_variables(N, Term, Bound, OldList, NewList).
 617
 618free_variables(0, _, _, VarList, VarList) :- !.
 619free_variables(N, Term, Bound, OldList, NewList) :-
 620    arg(N, Term, Argument),
 621    free_variables(Argument, Bound, OldList, MidList),
 622    M is N-1,
 623    !,
 624    free_variables(M, Term, Bound, MidList, NewList).
 625
 626%   explicit_binding checks for goals known to existentially quantify
 627%   one or more variables.  In particular \+ is quite common.
 628
 629explicit_binding(\+ _Goal,             Bound, fail,     Bound      ) :- !.
 630explicit_binding(not(_Goal),           Bound, fail,     Bound      ) :- !.
 631explicit_binding(Var^Goal,             Bound, Goal,     Bound+Var) :- !.
 632explicit_binding(setof(Var,Goal,Set),  Bound, Goal-Set, Bound+Var) :- !.
 633explicit_binding(bagof(Var,Goal,Bag),  Bound, Goal-Bag, Bound+Var) :- !.
 634
 635%!  term_is_free_of(+Term, +Var) is semidet.
 636%
 637%   True if Var does not appear  in   Term.  This has been rewritten
 638%   from the DEC10 library source   to exploit our non-deterministic
 639%   arg/3.
 640
 641term_is_free_of(Term, Var) :-
 642    \+ var_in_term(Term, Var).
 643
 644var_in_term(Term, Var) :-
 645    Var == Term,
 646    !.
 647var_in_term(Term, Var) :-
 648    compound(Term),
 649    arg(_, Term, Arg),
 650    var_in_term(Arg, Var),
 651    !.
 652
 653
 654%!  list_is_free_of(+List, +Var) is semidet.
 655%
 656%   True if Var is not in List.
 657
 658list_is_free_of([Head|Tail], Var) :-
 659    Head \== Var,
 660    !,
 661    list_is_free_of(Tail, Var).
 662list_is_free_of([], _).
 663
 664
 665%       term_variables(+Term, +Vars0, -Vars) is det.
 666%
 667%       True if Vars is the union of variables in Term and Vars0.
 668%       We cannot have this as term_variables/3 is already defined
 669%       as a difference-list version of term_variables/2.
 670
 671%term_variables(Term, Vars0, Vars) :-
 672%       term_variables(Term+Vars0, Vars).
 673
 674
 675%!  sandbox:safe_meta(+Goal, -Called) is semidet.
 676%
 677%   Declare the aggregate meta-calls safe. This cannot be proven due
 678%   to the manipulations of the argument Goal.
 679
 680:- multifile sandbox:safe_meta_predicate/1.
 681
 682sandbox:safe_meta_predicate(aggregate:foreach/2).
 683sandbox:safe_meta_predicate(aggregate:aggregate/3).
 684sandbox:safe_meta_predicate(aggregate:aggregate/4).
 685sandbox:safe_meta_predicate(aggregate:aggregate_all/3).
 686sandbox:safe_meta_predicate(aggregate:aggregate_all/4).