1% ----------------------------------------------------------------------
2% BEGIN LICENSE BLOCK
3% Version: CMPL 1.1
4%
5% The contents of this file are subject to the Cisco-style Mozilla Public
6% License Version 1.1 (the "License"); you may not use this file except
7% in compliance with the License.  You may obtain a copy of the License
8% at www.eclipse-clp.org/license.
9%
10% Software distributed under the License is distributed on an "AS IS"
11% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
12% the License for the specific language governing rights and limitations
13% under the License.
14%
15% The Original Code is  The ECLiPSe Constraint Logic Programming System.
16% The Initial Developer of the Original Code is  Cisco Systems, Inc.
17% Portions created by the Initial Developer are
18% Copyright (C) 1992-2006 Cisco Systems, Inc.  All Rights Reserved.
19%
20% Contributor(s): ECRC GmbH
21% Contributor(s): IC-Parc, Imperal College London
22%
23% END LICENSE BLOCK
24%
25% System:	ECLiPSe Constraint Logic Programming System
26% Version:	$Id: suspend.pl,v 1.4 2010/04/04 08:13:37 jschimpf Exp $
27% ----------------------------------------------------------------------
28
29%
30% SEPIA PROLOG MODULE
31%
32% IDENTIFICATION:	suspend.pl
33%
34% AUTHOR:		Micha Meier
35%			Joachim Schimpf
36%
37% CONTENTS:		Coroutining with three suspension lists:
38%
39%			- one for goals to be woken on instantiation only
40%			- one for binding with any metaterm
41%			- one for general "constrained-ness"
42%
43%			The code for metaterm handlers should be used
44%			as template for writing user-defined extensions;
45%			simply replace 'suspend' in the predicate names by
46%			the name of the attribute and re-use the code.
47%
48
49:- module(suspend, [], sepia_kernel).
50
51% This structure is already defined and exported in the kernel
52%:- export struct(suspend(inst, constrained, bound)).
53
54:- comment(categories, ["Constraints","Algorithms"]).
55:- comment(summary,
56    "Lazy-checking versions of arithmetic primitives, and the suspend-attribute").
57:- comment(date, "$Date: 2010/04/04 08:13:37 $").
58:- comment(copyright, "Cisco Systems, Inc").
59:- comment(author, "Micha Meier, ECRC, Joachim Schimpf, ECRC and IC-Parc").
60:- comment(desc, html("\
61    This library provides the following:
62<UL>
63    <LI>the <B>suspend</B> pseudo-solver for general arithmetic
64    <LI>the <B>suspend</B> attribute
65</UL>
66    The suspend pseudo-solver for arithmetic comparisons simply consists
67    of suspending (lazy-checking) versions of all arithmetic comparisons
68    (&gt;/2, #&gt;/2, etc).  These all suspend until all their variables
69    have been instantiated, then they wake up and test the condition,
70    succeeding or failing as appropriate. Together with a search routine,
71    this provides the means to implement simple test-and-generate algorithms.
72<P>
73    The suspend-attribute is a basis for the implementation of similar
74    user-defined data-driven computations that react to variable instantiation,
75    variable binding, or general variable-constraining events.  The suspend-
76    attribute defines the following three suspension lists (and thus waking
77    conditions) for a variable:
78<DL>
79    <DT><STRONG>inst</STRONG><DD>
80    	woken when variable is instantiated.
81    <DT><STRONG>bound</STRONG><DD>
82    	woken when variable is bound, even to an other variable.
83    <DT><STRONG>constrained</STRONG><DD>
84    	woken when variable is (further) constrained.  This is
85	triggered by the notify_constrained built-in.
86</DL>
87    These three lists can be used as waking conditions in the suspend/3,4
88    built-in. Variables using the suspend-attribute do not have to be
89    declared specially, the attribute is implicitly created when needed.
90    ")).
91
92:- import
93	(:@)/3,
94	eval/3,
95	get_flag_body/4,
96	insert_suspension/4,
97	setarg/3,
98	suspensions_to_goals/3
99    from sepia_kernel.
100
101%----------------------------------------------------------------
102% attribute declaration
103%----------------------------------------------------------------
104
105% CAUTION: when changing handlers, update meta.pl accordingly!!!
106:- meta_attribute(suspend, [
107	unify:			unify_suspend/2,
108	suspensions:		suspensions_suspend/3,
109	delayed_goals_number:	delayed_goals_number_suspend/2
110	]).
111
112%----------------------------------------------------------------
113% unify handler
114%----------------------------------------------------------------
115
116:- pragma(system).
117:- pragma(nodebug).
118
119% unify_suspend(+Term, Attribute)
120unify_suspend(_, Attr) :-
121    /*** ANY + VAR ***/
122    var(Attr).			% Ignore if no attribute for this extension
123unify_suspend(Term, Attr) :-
124    compound(Attr),
125    unify_term_suspend(Term, Attr).
126
127
128% We wake every time a variable is touched.
129:- mode unify_term_suspend(?, +).
130unify_term_suspend(Term, AttrX) :-
131    atomic(Term),		% The metaterm was instantiated, wake all
132    /*** NONVAR + META ***/
133    AttrX = suspend{bound:B, inst:I, constrained:C},
134    % schedule_woken/1 is faster than schedule_suspensions/2 but can
135    % be used only if the lists are no longer needed after waking
136    schedule_woken(C),
137    schedule_woken(B),
138    schedule_woken(I).
139unify_term_suspend(Term, AttrX) :-
140    compound(Term),		% The metaterm was instantiated, wake all
141    /*** NONVAR + META ***/
142    schedule_suspensions(constrained of suspend, AttrX),
143    AttrX = suspend{bound:B, inst:I, constrained:Cnew},
144    schedule_woken(B),
145    schedule_woken(I),
146    % the constrained list may still contain demons after scheduling,
147    % forward them to the variables in the bound term
148    ( Cnew = [] ->
149    	true
150    ;
151	term_variables(Term, Vars),
152	( Vars = [] -> true ; forward_constrained_list(Cnew, Vars) )
153    ).
154unify_term_suspend(Y{AttrY}, AttrX) :-
155    -?->
156    unify_suspend_suspend(Y, AttrX, AttrY).
157
158unify_suspend_suspend(_, AttrX, AttrY) :-
159    var(AttrY),			% No attribute for this extension
160    /*** VAR + META ***/
161    AttrX = AttrY.		% Keep both lists, do not wake
162unify_suspend_suspend(_, AttrX, AttrY) :-
163    nonvar(AttrY),
164    /*** META + META ***/
165    AttrX = suspend{inst:XI-YI,  bound:BX, constrained:CX},
166    AttrY = suspend{inst:YI-YI0, bound:BY, constrained:CY},
167    setarg(inst of suspend, AttrY, XI-YI0),
168
169    % merge the bound-lists and wake those suspensions that were in both lists
170    merge_intersect(BX, BY, BXY, BoundSuspsToWake),
171    schedule_woken(BoundSuspsToWake),
172    setarg(bound of suspend, AttrY, BXY),
173
174    % treat the constrained-lists exactly like the bounds-lists
175    merge_intersect(CX, CY, CXY, ConstrSuspsToWake),
176    schedule_woken(ConstrSuspsToWake),
177    setarg(constrained of suspend, AttrY, CXY).
178
179
180forward_constrained_list([], _Vars).
181forward_constrained_list([Susp|Susps], Vars) :-
182    ( is_suspension(Susp) ->
183	insert_suspension(Vars, Susp, constrained of suspend)
184    ;
185	true	% dead suspension
186    ),
187    forward_constrained_list(Susps, Vars).
188
189
190:- mode merge_intersect(+,+,-,-).
191merge_intersect([], L2, L2, []) :- !.
192merge_intersect(L1, [], L1, []) :- !.
193merge_intersect(L1, L2, Merged, Common) :-
194	% sort and remove duplicates (descending, because the lists are
195	% likely to be already in descending order, due to new suspensions
196	% always being inserted in front)
197	sort(0, >, L1, S1),
198	sort(0, >, L2, S2),
199	% merge and extract common elements
200	merge(0, >=, S1, S2, S12),
201	S12 = [S|Ss],
202	split_unique_common(S, Ss, Merged, Common).
203
204    split_unique_common(S1, [], [S1], []).
205    split_unique_common(S1, [S2|Ss], Merged, Common) :-
206    	( S1 == S2 ->
207	    Common = [S1|Common0],
208	    split_unique_common(S2, Ss, Merged, Common0)
209	;
210	    Merged = [S1|Merged0],
211	    split_unique_common(S2, Ss, Merged0, Common)
212	).
213
214
215%----------------------------------------------------------------
216% suspensions handler
217%----------------------------------------------------------------
218
219suspensions_suspend(_{Attr}, Goals, G0) :-
220    -?->
221    susp_suspend(Attr, Goals, G0).
222
223susp_suspend(AttrX, Susps, Susps) :- var(AttrX), !.
224susp_suspend(suspend{inst:I-_, bound:B, constrained:C}, [I,B,C|Ss], Ss).
225
226
227%----------------------------------------------------------------
228% delayed goals number handler
229%----------------------------------------------------------------
230
231delayed_goals_number_suspend(_{AttrX}, N) :-
232    -?->
233    dgn_suspend(AttrX, N).
234
235dgn_suspend(AttrX, 0) :-
236    /*** VAR ***/
237    var(AttrX),
238    !.
239dgn_suspend(suspend{inst:I-_, bound:B, constrained:C}, N) :-
240    /*** META ***/
241    count_active_suspensions(I, 0, N0),
242    count_active_suspensions(B, N0, N1),
243    count_active_suspensions(C, N1, N).
244
245count_active_suspensions(Susps, N0, N) :-
246    var(Susps),
247    !,
248    N = N0.
249count_active_suspensions([], N, N).
250count_active_suspensions([Susp|Susps], N0, N) :-
251    ( is_suspension(Susp) ->
252	    N1 is N0 + 1
253    ;
254	    N1 = N0
255    ),
256    count_active_suspensions(Susps, N1, N).
257
258
259:- untraceable
260	unify_suspend/2,
261	suspensions_suspend/3,
262	delayed_goals_number_suspend/2.
263
264
265%----------------------------------------------------------------
266% Sepia's delay clauses (backward compatibility)
267% Source transformation for if/2
268%----------------------------------------------------------------
269
270:- export tr_if_suspend/3.
271:- export macro((if)/2, tr_if_suspend/3, [clause]).
272
273:- mode tr_if_suspend(+, -, +).
274tr_if_suspend(Clause, (Head :- -?-> NewBody, Susp), _M) :-
275	Clause = no_macro_expansion(delay Head if Body),
276	translate_goal(Body, NewBody, VarList, [], BVarList, []),
277	check_varlists(VarList, BVarList, Clause),
278	strip_varlist(VarList, VarList1),
279	strip_varlist(BVarList, BVarList1),
280	handle_suspension(Head, VarList1, BVarList1, Susp).
281
282:- mode translate_goal(?, -, ?, -, ?, -).
283translate_goal(Goal,	Goal,	Vars,	Vars, BV, BV) :- var(Goal), !.
284translate_goal((Goal, Goals), (NewGoal, NewGoals), Out, In, BOut, BIn) :- !,
285	translate_goal(Goal, NewGoal, Mid, In, BMid, BIn),
286	translate_goal(Goals, NewGoals, Out, Mid, BOut, BMid).
287translate_goal((Goal -> Goals), (NewGoal -> NewGoals), Out, In, BOut, BIn) :- !,
288	translate_goal(Goal, NewGoal, Mid, In, BMid, BIn),
289	translate_goal(Goals, NewGoals, Out, Mid, BOut, BMid).
290translate_goal((Goal ; Goals), (NewGoal, Out=VarsL, BOut = BVarsL ;
291				NewGoals, Out=VarsR, BOut = BVarsR),
292		[Out], In, [BOut], BIn) :- !,
293	translate_goal(Goal, NewGoal, VarsL0, In, BVarsL0, BIn),
294	check_varlists(VarsL0, BVarsL0, Goal),
295	strip_varlist(VarsL0, VarsL),
296	strip_varlist(BVarsL0, BVarsL),
297	translate_goal(Goals, NewGoals, VarsR0, In, BVarsR0, BIn),
298	check_varlists(VarsR0, BVarsR0, Goal),
299	strip_varlist(VarsR0, VarsR),
300	strip_varlist(BVarsR0, BVarsR).
301translate_goal(var(X),		var(X),			[X|Out],    Out, V,V) :-
302	!.
303translate_goal(X \== Y,
304		(sepia_kernel: \==(X, Y, Vars),
305		    ( Vars = [Var] -> Out = [Var|In], BOut = BIn
306		    ; BOut = [Vars|BIn], Out = In
307		    )
308		),
309		[Out], In, [BOut], BIn) :- !.
310translate_goal(nonground(X),	nonground(X, Var),	[Var|Out],  Out, V,V) :-
311	!.
312translate_goal(nonground(N, X),	nonground(N, X, Vars),	[Vars|Out], Out, V,V) :-
313	!.
314translate_goal(Goal,	Goal, 	Vars,	Vars, BVars, BVars).
315
316handle_suspension(Head, List, BL, (!, make_suspension(Head, 0, S), Body)) :-
317	handle_i_susp(S, List, BL, Body).
318
319handle_i_susp(Susp, [], BList, Body) :-
320	-?->
321	!,
322	bound_suspension(Susp, BList, Body).
323handle_i_susp(Susp, List, [], Body) :-
324	-?->
325	!,
326	inst_suspension(Susp, List, Body).
327handle_i_susp(Susp, List, BList, (BI, BB)) :-
328	inst_suspension(Susp, List, BI),
329	bound_suspension(Susp, BList, BB).
330
331inst_suspension(S, List,
332	insert_suspension(List, S, inst of suspend, suspend)).
333
334bound_suspension(S, List,
335	insert_suspension(List, S, bound of suspend, suspend)).
336
337
338:- mode check_varlists(+,+,?).
339check_varlists([], [], Culprit) :- !,
340	error(272, Culprit).
341check_varlists(_, _, _).
342
343:- mode strip_varlist(+,-).
344strip_varlist([], []) :- !.
345strip_varlist([X], X) :- !.	% avoid list if single element
346strip_varlist(L, L).
347
348
349%----------------------------------------------------------------------
350% Delaying arithmetic
351%----------------------------------------------------------------------
352
353:- export
354	op(750, fx, [neg]),
355	op(760, yfx, [and]),
356	op(770, yfx, [or]),
357	op(780, yfx, [=>]),
358	op(700, xfx, [#::,$::,$=,$\=,$>=,$=<,$>,$<]).
359
360:- export
361	:: /2,
362	$:: /2,
363	#:: /2,
364	integers/1,
365	reals/1,
366	=:=  /2,
367	=\= /2,
368	>= /2,
369	=< /2,
370	> /2,
371	< /2,
372	$=  /2,
373	$\= /2,
374	$>= /2,
375	$=< /2,
376	$>  /2,
377	$<  /2,
378	#=  /2,
379	#\= /2,
380	#>= /2,
381	#=< /2,
382	#>  /2,
383	#<  /2,
384	and/2,
385	or/2,
386	=> /2,
387	(neg)/1.
388
389
390    % integers(X) - X is an integer or a list of integers
391integers(Tail) :- var(Tail), !,
392	suspend(integers(Tail), 2, Tail->inst).
393integers([]) :- !.
394integers([X|Xs]) :- !,
395	one_integer(X),
396	integers(Xs).
397integers(X) :- integer(X).
398
399    one_integer(X) :- var(X),
400	suspend(integer(X), 2, X->inst).
401    one_integer(X) :- integer(X).
402
403
404    % reals(X) - X is a number or a list of numbers
405reals(Tail) :- var(Tail), !,
406	suspend(reals(Tail), 2, Tail->inst).
407reals([]) :- !.
408reals([X|Xs]) :- !,
409	one_number(X),
410	reals(Xs).
411reals(X) :- number(X).
412
413    one_number(X) :- var(X),
414	suspend(number(X), 2, X->inst).
415    one_number(X) :- number(X).
416
417
418:- tool((::)/2, '::_body'/3).
419:- tool(($::)/2, '$::_body'/3).
420:- tool((#::)/2, '#::_body'/3).
421:- tool((#=)/2, '#=_body'/3).
422:- tool((#\=)/2, '#\\=_body'/3).
423:- tool((#=<)/2, '#=<_body'/3).
424:- tool((#>=)/2, '#>=_body'/3).
425:- tool((#<)/2, '#<_body'/3).
426:- tool((#>)/2, '#>_body'/3).
427:- tool(($=)/2, '$=_body'/3).
428:- tool(($\=)/2, '$\\=_body'/3).
429:- tool(($=<)/2, '$=<_body'/3).
430:- tool(($>=)/2, '$>=_body'/3).
431:- tool(($<)/2, '$<_body'/3).
432:- tool(($>)/2, '$>_body'/3).
433:- tool((=:=)/2, '=:=_body'/3).
434:- tool((=\=)/2, '=\\=_body'/3).
435:- tool((=<)/2, '=<_body'/3).
436:- tool((>=)/2, '>=_body'/3).
437:- tool((<)/2, '<_body'/3).
438:- tool((>)/2, '>_body'/3).
439:- tool((and)/2, 'and_body'/3).
440:- tool((or)/2, 'or_body'/3).
441:- tool((=>)/2, '=>_body'/3).
442:- tool((neg)/1, 'neg_body'/2).
443
444'::_body'(X, Range, M)	:- delay_until_ground(X :: Range, M).
445'$::_body'(X, Range, M)	:- delay_until_ground(X $:: Range, M).
446'#::_body'(X, Range, M)	:- delay_until_ground(X #:: Range, M).
447'#=_body'(X, Y, M)	:- delay_until_ground(X #= Y, M).
448'#\\=_body'(X, Y, M)	:- delay_until_ground(X #\= Y, M).
449'#=<_body'(X, Y, M)	:- delay_until_ground(X #=< Y, M).
450'#>=_body'(X, Y, M)	:- delay_until_ground(X #>= Y, M).
451'#<_body'(X, Y, M)	:- delay_until_ground(X #< Y, M).
452'#>_body'(X, Y, M)	:- delay_until_ground(X #> Y, M).
453'$=_body'(X, Y, M)	:- delay_until_ground(X =:= Y, M).
454'$\\=_body'(X, Y, M)	:- delay_until_ground(X =\= Y, M).
455'$=<_body'(X, Y, M)	:- delay_until_ground(X =< Y, M).
456'$>=_body'(X, Y, M)	:- delay_until_ground(X >= Y, M).
457'$<_body'(X, Y, M)	:- delay_until_ground(X < Y, M).
458'$>_body'(X, Y, M)	:- delay_until_ground(X > Y, M).
459'=:=_body'(X, Y, M)	:- delay_until_ground(X =:= Y, M).
460'=\\=_body'(X, Y, M)	:- delay_until_ground(X =\= Y, M).
461'=<_body'(X, Y, M)	:- delay_until_ground(X =< Y, M).
462'>=_body'(X, Y, M)	:- delay_until_ground(X >= Y, M).
463'<_body'(X, Y, M)	:- delay_until_ground(X < Y, M).
464'>_body'(X, Y, M)	:- delay_until_ground(X > Y, M).
465'and_body'(X, Y, M)	:- delay_until_ground(X and Y, M).
466'or_body'(X, Y, M)	:- delay_until_ground(X or Y, M).
467'=>_body'(X, Y, M)	:- delay_until_ground(X => Y, M).
468'neg_body'(X, M)	:- delay_until_ground(neg X, M).
469
470
471    delay_until_ground(Goal, M) :-
472	( nonground(Goal, Var) ->
473	    suspend(delay_until_ground(Goal, Susp, M), 2, Var->inst, Susp)
474	;
475	    ground_check(Goal, M)
476	).
477
478    :- demon delay_until_ground/3.
479    delay_until_ground(Goal, Susp, M) :-
480	( nonground(Goal, Var) ->
481	    insert_suspension(Var, Susp, inst of suspend)
482	;
483	    kill_suspension(Susp),
484	    ground_check(Goal, M)
485	).
486
487
488% Note on the #-constraints:  Implementation via
489%	X #? ...  :-  XI is X, integer(XI), ...
490% is not quite right, but almost.  # is supposed to impose integrality on
491% all vars in X (except those within user-defined functions?), but we only
492% check whether the result is integral. This only breaks down for fix/1,
493% sgn/1, numerator/1 and denominator/1 (which seems acceptable), because
494% all other functions propagate non-integrality through.
495
496ground_check(Xs :: Range, M) :-
497	eval_range(Xs, Range, From, To, Type, M),
498	check_range(Xs, From, To, Type).
499ground_check(Xs $:: Range, M) :-
500	eval_range(Xs, Range, From, To, _Type, M),
501	check_range(Xs, From, To, number).
502ground_check(Xs #:: Range, M) :-
503	eval_range(Xs, Range, From, To, Type, M),
504	( Type == integer ->
505	    check_range(Xs, From, To, Type)
506	;
507	    error(5, Xs :: Range, M)
508	).
509ground_check(integers(Xs), _M)	:- check_integers(Xs).
510ground_check(reals(Xs), _M)	:- check_numbers(Xs).
511ground_check(X =:= Y, M)	:- :@(sepia_kernel, (X =:= Y), M).
512ground_check(X =\= Y, M)	:- :@(sepia_kernel, (X =\= Y), M).
513ground_check(X >= Y, M)		:- :@(sepia_kernel, (X >= Y), M).
514ground_check(X =< Y, M)		:- :@(sepia_kernel, (X =< Y), M).
515ground_check(X > Y, M)		:- :@(sepia_kernel, (X > Y), M).
516ground_check(X < Y, M)		:- :@(sepia_kernel, (X < Y), M).
517ground_check(X $= Y, M)		:- :@(sepia_kernel, (X =:= Y), M).
518ground_check(X $\= Y, M)	:- :@(sepia_kernel, (X =\= Y), M).
519ground_check(X $>= Y, M)	:- :@(sepia_kernel, (X >= Y), M).
520ground_check(X $=< Y, M)	:- :@(sepia_kernel, (X =< Y), M).
521ground_check(X $> Y, M)		:- :@(sepia_kernel, (X > Y), M).
522ground_check(X $< Y, M)		:- :@(sepia_kernel, (X < Y), M).
523ground_check(X #= Y, M)		:- eval(X,XI,M), integer(XI), eval(Y,YI,M), integer(YI), :@(sepia_kernel, (XI =:= YI), M).
524ground_check(X #\= Y, M)	:- eval(X,XI,M), integer(XI), eval(Y,YI,M), integer(YI), :@(sepia_kernel, (XI =\= YI), M).
525ground_check(X #>= Y, M)	:- eval(X,XI,M), integer(XI), eval(Y,YI,M), integer(YI), :@(sepia_kernel, (XI >= YI), M).
526ground_check(X #=< Y, M)	:- eval(X,XI,M), integer(XI), eval(Y,YI,M), integer(YI), :@(sepia_kernel, (XI =< YI), M).
527ground_check(X #> Y, M)		:- eval(X,XI,M), integer(XI), eval(Y,YI,M), integer(YI), :@(sepia_kernel, (XI > YI), M).
528ground_check(X #< Y, M)		:- eval(X,XI,M), integer(XI), eval(Y,YI,M), integer(YI), :@(sepia_kernel, (XI < YI), M).
529ground_check(X and Y, M)	:- eval(X,XI,M), bool(XI), eval(Y,YI,M), bool(YI), sepia_kernel:(XI+YI > 1).
530ground_check(X or Y, M)		:- eval(X,XI,M), bool(XI), eval(Y,YI,M), bool(YI), sepia_kernel:(XI+YI > 0).
531ground_check(X => Y, M)		:- eval(X,XI,M), bool(XI), eval(Y,YI,M), bool(YI), sepia_kernel:(YI-XI >= 0).
532ground_check(neg X, M)		:- eval(X,0,M).
533
534
535:- mode eval_range(?,++,-,-,-,+).
536eval_range(_X, From0..To0, From, To, Type, M) :- !,
537	eval(From0, From, M),
538	eval(To0, To, M),
539	( integer(From), integer(To) ->
540	    Type = integer
541	;
542	    Type = number
543	).
544eval_range(X, Range, _From, _To, _Type, M) :-
545	error(5, X::Range, M).
546
547
548:- mode check_range(++,+,+,+).
549check_range(X, From, To, Type) :- number(X), !,
550	check_range_number(X, From, To, Type).
551check_range([], _From, _To, _Type) :- !.
552check_range(List, From, To, Type) :- List = [_|_], !,
553	check_range_list(List, From, To, Type).
554check_range(subscript(Array,Index), From, To, Type) :- !,
555	subscript(Array, Index, Elems),
556	check_range(Elems, From, To, Type).
557check_range(X, From, To, _Type) :- !,
558	error(5, X::From..To).
559
560    check_range_list([], _From, _To, _Type) :- !.
561    check_range_list([X|Xs], From, To, Type) :- !,
562	check_range_number(X, From, To, Type),
563	check_range_list(Xs, From, To, Type).
564    check_range_list(X, From, To, _Type) :-
565    	error(5, X::From..To).
566
567    check_range_number(X, From, To, integer) :-
568	sepia_kernel:(From =< X),
569	sepia_kernel:(X =< To),
570    	integer(X).
571    check_range_number(X, From, To, number) :-
572	sepia_kernel:(From =< X),
573	sepia_kernel:(X =< To).
574
575
576:- mode check_integers(++).
577check_integers([]) :- !.
578check_integers([X|Xs]) :- !,
579	integer(X),
580	check_integers(Xs).
581check_integers(X) :-
582	error(5, integers(X)).
583
584:- mode check_numbers(++).
585check_numbers([]) :- !.
586check_numbers([X|Xs]) :- !,
587	number(X),
588	check_numbers(Xs).
589check_numbers(X) :-
590	error(5, reals(X)).
591
592bool(0).
593bool(1).
594
595
596%- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
597% Reified versions
598%
599% Problem: what does #=(X,Y,B) mean?
600%
601% - The proper reification of #=(X,Y) would be
602%
603%    #=(X,Y,B) :- integers(X,BX), integers(Y,BY), $=(X,Y,BZ), $=(BX+BY+BZ,3,B).
604%
605%   but this is not really what a user expects.
606%
607% - instead it means
608%
609%    #=(X,Y,B) :- integers(X), integers(Y), $=(X,Y,B).
610%- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
611
612:- export
613	::  /3,
614	$::  /3,
615	#::  /3,
616	=:=  /3,
617	=\= /3,
618	>  /3,
619	<  /3,
620	>= /3,
621	=< /3,
622	$=  /3,
623	$\= /3,
624	$>  /3,
625	$<  /3,
626	$>= /3,
627	$=< /3,
628	#=  /3,
629	#\= /3,
630	#>= /3,
631	#=< /3,
632	#<  /3,
633	#>  /3,
634	and/3,
635	or/3,
636	=> /3,
637	(neg)/2.
638
639:- tool((::)/3, '::_body'/4).
640:- tool(($::)/3, '$::_body'/4).
641:- tool((#::)/3, '#::_body'/4).
642:- tool((#=)/3, '#=_body'/4).
643:- tool((#\=)/3, '#\\=_body'/4).
644:- tool((#=<)/3, '#=<_body'/4).
645:- tool((#>=)/3, '#>=_body'/4).
646:- tool((#<)/3, '#<_body'/4).
647:- tool((#>)/3, '#>_body'/4).
648:- tool(($=)/3, '$=_body'/4).
649:- tool(($\=)/3, '$\\=_body'/4).
650:- tool(($=<)/3, '$=<_body'/4).
651:- tool(($>=)/3, '$>=_body'/4).
652:- tool(($<)/3, '$<_body'/4).
653:- tool(($>)/3, '$>_body'/4).
654:- tool((=:=)/3, '=:=_body'/4).
655:- tool((=\=)/3, '=\\=_body'/4).
656:- tool((=<)/3, '=<_body'/4).
657:- tool((>=)/3, '>=_body'/4).
658:- tool((<)/3, '<_body'/4).
659:- tool((>)/3, '>_body'/4).
660:- tool((and)/3, 'and_body'/4).
661:- tool((or)/3, 'or_body'/4).
662:- tool((=>)/3, '=>_body'/4).
663:- tool((neg)/2, 'neg_body'/3).
664
665
666'::_body'(X, Range, B, M) :- one_number(X), reified(X :: Range, B, M).
667'$::_body'(X, Range, B, M) :- one_number(X), reified(X $:: Range, B, M).
668'#::_body'(X, Range, B, M) :- one_number(X), reified(X #:: Range, B, M).
669'#=_body'(X, Y, B, M)	:- reified(X #= Y, B, M).
670'#\\=_body'(X, Y, B, M)	:- reified(X #\= Y, B, M).
671'#=<_body'(X, Y, B, M)	:- reified(X #=< Y, B, M).
672'#>=_body'(X, Y, B, M)	:- reified(X #>= Y, B, M).
673'#<_body'(X, Y, B, M)	:- reified(X #< Y, B, M).
674'#>_body'(X, Y, B, M)	:- reified(X #> Y, B, M).
675'$=_body'(X, Y, B, M)	:- reified(X $= Y, B, M).
676'$\\=_body'(X, Y, B, M)	:- reified(X $\= Y, B, M).
677'$=<_body'(X, Y, B, M)	:- reified(X $=< Y, B, M).
678'$>=_body'(X, Y, B, M)	:- reified(X $>= Y, B, M).
679'$<_body'(X, Y, B, M)	:- reified(X $< Y, B, M).
680'$>_body'(X, Y, B, M)	:- reified(X $> Y, B, M).
681'=:=_body'(X, Y, B, M)	:- reified(X =:= Y, B, M).
682'=\\=_body'(X, Y, B, M)	:- reified(X =\= Y, B, M).
683'=<_body'(X, Y, B, M)	:- reified(X =< Y, B, M).
684'>=_body'(X, Y, B, M)	:- reified(X >= Y, B, M).
685'<_body'(X, Y, B, M)	:- reified(X < Y, B, M).
686'>_body'(X, Y, B, M)	:- reified(X > Y, B, M).
687'and_body'(X, Y, B, M)	:- reified(X and Y, B, M).
688'or_body'(X, Y, B, M)	:- reified(X or Y, B, M).
689'=>_body'(X, Y, B, M)	:- reified(X => Y, B, M).
690'neg_body'(X, B, M)	:- reified(neg X, B, M).
691
692
693reified(Goal, Bool, M) :-
694	( nonground(Goal, Var) ->
695	    suspend(delay_reified(Goal, Bool, Susp, M), 2, Var->inst, Susp)
696	;
697	    ( ground_check(Goal, M) -> Bool=1 ; Bool=0 )	% Goal is ground
698	).
699
700    :- demon delay_reified/4.
701    delay_reified(Goal, Bool, Susp, M) :-
702	( nonground(Goal, Var) ->
703	    insert_suspension(Var, Susp, inst of suspend)
704	;
705	    kill_suspension(Susp),
706	    ( ground_check(Goal, M) -> Bool=1 ; Bool=0 )	% Goal is ground
707	).
708
709
710%- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
711% Pretty printing
712%- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
713
714:- export
715	portray_delayed_goals/2,
716	portray(delay_until_ground/3, portray_delayed_goals/2, [goal]).
717
718portray_delayed_goals(delay_until_ground(Goal, _Susp, _M), Pretty) :- -?-> !,
719	Pretty = suspend:Goal.
720
721
722:- export
723	portray_delay_reified/2,
724	portray(delay_reified/4, portray_delay_reified/2, [goal]).
725
726portray_delay_reified(delay_reified(Goal, Bool, _Susp, _M), Pretty) :- -?-> !,
727	Goal =.. [Op|Args],
728	append(Args, [Bool], NewArgs),
729	ReifGoal =.. [Op|NewArgs],
730	Pretty = suspend:ReifGoal.
731
732
733%--------------------------------------------------------------------
734% Comments doc
735%--------------------------------------------------------------------
736
737:- comment((::)/2, [
738    summary: "Range constraint with optional integrality constraint",
739    template: "?Vars :: ?Range",
740    args: ["Vars":  "Variable or number, or a list of variables or numbers",
741           "Range": "Variable or Lo..Hi, where Lo and Hi are variables or numeric expressions"
742          ],
743    see_also:[($::)/2, (#::)/2, (::)/3, integers/1, reals/1],
744    fail_if: "Vars contains numbers that do not fall within Range, or violate the optional integrality constraint.",
745    eg:"
746    % with integrality constraint
747    ?- X :: 1 .. 5, X = 3.
748    X = 3
749    Yes (0.00s cpu)
750
751    ?- X :: 1 .. 5, X = 6.
752    No (0.00s cpu)
753
754    ?- X :: 1 .. 5, X = 3.0.
755    No (0.00s cpu)
756
757    % without integrality constraint
758    ?- X :: 1.0 .. 5.0, X = 3.
759    X = 3
760    Yes (0.00s cpu)
761
762    ?- X :: 1.0 .. 5.0, X = 3.0.
763    X = 3.0
764    Yes (0.00s cpu)
765    ",
766    desc: html("\
767   This constraint suspends until its arguments are ground. It then succeeds
768   iff all the elements of the list Vars are numbers within the range
769   specified by Range.
770<P>
771   The range must eventually be in the form Lo..Hi, where Lo and Hi are
772   expressions evaluating to numbers. If both are integers, then Vars must
773   also be integers (integer or list of integers).")]
774).
775
776:- comment((#::)/2, [
777    summary: "Range constraint combined with integrality constraint",
778    template: "?Vars #:: ?Range",
779    see_also:[($::)/2, (::)/2, (#::)/3, integers/1, reals/1],
780    args: ["Vars":  "Variable or integer, or a list of variables or integers",
781           "Range": "Variable or Lo..Hi, where Lo and Hi are variables or integer expressions"
782          ],
783    exceptions:[5 : "Range contains non-integers."],
784    fail_if: "Vars contains non-integers or integers that do not fall within Range.",
785    eg:"
786    ?- X #:: 1 .. 5, X = 3.
787    X = 3
788    Yes (0.00s cpu)
789
790    ?- X #:: 1 .. 5, X = 6.
791    No (0.00s cpu)
792
793    ?- X #:: 1 .. 5, X = 3.0.
794    No (0.00s cpu)
795    ",
796    desc: html("\
797   This constraint suspends until its arguments are ground. It then succeeds
798   iff all the elements of the list Vars are integers within the range
799   specified by Range.
800<P>
801   The range must eventually be in the form Lo..Hi, where Lo and Hi are
802   expressions evaluating to integers.")]
803).
804
805:- comment(($::)/2, [
806    summary: "Pure range constraint",
807    template: "?Vars #:: ?Range",
808    args: ["Vars":  "Variable or number, or a list of variables or numbers",
809           "Range": "Variable or Lo..Hi, where Lo and Hi are variables or numeric expressions"
810          ],
811    see_also:[(::)/2, (#::)/2, ($::)/3, integers/1, reals/1],
812    fail_if: "Vars contains numbers that do not fall within Range.",
813    eg:"
814    ?- X $:: 1 .. 5, X = 3.0.
815    X = 3.0
816    Yes (0.00s cpu)
817
818    ?- X $:: 1 .. 5, X = 3.
819    X = 3
820    Yes (0.00s cpu)
821
822    ?- X $:: 1.0 .. 5.0, X = 3.
823    X = 3
824    Yes (0.00s cpu)
825
826    ?- X $:: 1.0 .. 5.0, X = 3.0.
827    X = 3.0
828    Yes (0.00s cpu)
829    ",
830    desc: html("\
831   This constraint suspends until its arguments are ground. It then succeeds
832   iff all the elements of the list Vars are numbers within the range
833   specified by Range.
834<P>
835   The range must eventually be in the form Lo..Hi, where Lo and Hi are
836   expressions evaluating to numbers. The type of these numbers is irrelevant.")]
837).
838
839:- comment((::)/3, [
840    summary: "Reified range constraint with optional integrality constraint",
841    args: ["Var":  "Variable or number",
842           "Range": "Variable or Lo..Hi, where Lo and Hi are variables or numeric expressions",
843           "Bool": "Variable, 0 or 1"
844          ],
845    see_also:[(::)/2],
846    eg:"
847    ?- ::(X, 1 .. 5, B), X = 3.
848    B = 1
849    X = 3
850    Yes (0.00s cpu)
851
852    ?- ::(X, 1.0 .. 5.0, B), X = 3.0.
853    B = 1
854    X = 3.0
855    Yes (0.00s cpu)
856
857    % range violated
858    ?- ::(X, 1 .. 5, B), X = 6.
859    B = 0
860    X = 6
861    Yes (0.00s cpu)
862
863    % integrality violated
864    ?- ::(X, 1 .. 5, B), X = 3.0.
865    B = 0
866    X = 3.0
867    Yes (0.00s cpu)
868    ",
869    desc: html("\
870    Reified version of ::/2, i.e. the truth value of the range constraint is
871    reflected in the value of the 0/1 variable Bool.
872<P>
873    This constraint suspends until its first two arguments are ground.
874    It then unifies Bool according to the truth value of the corresponding
875    ::/2 constraint.
876<P>
877    Note: as opposed to ::/2, the first argument cannot be a list.
878    ")]
879).
880
881:- comment((#::)/3, [
882    summary: "Reified range constraint combined with integrality constraint",
883    args: ["Vars":  "Variable or integer, or a list of variables or integers",
884           "Range": "Variable or Lo..Hi, where Lo and Hi are variables or integer expressions",
885           "Bool": "Variable, 0 or 1"
886          ],
887    see_also:[(#::)/2],
888    exceptions:[5 : "Range contains non-integers."],
889    eg:"
890    ?- #::(X, 1 .. 5, B), X = 3.
891    B = 1
892    X = 3
893    Yes (0.00s cpu)
894
895    % range violated
896    ?- #::(X, 1 .. 5, B), X = 6.
897    B = 0
898    X = 6
899    Yes (0.00s cpu)
900
901    % integrality violated
902    ?- #::(X, 1 .. 5, B), X = 3.0.
903    B = 0
904    X = 3.0
905    Yes (0.00s cpu)
906    ",
907    desc: html("\
908    Reified version of #::/2, i.e. the truth value of the range constraint is
909    reflected in the value of the 0/1 variable Bool.
910<P>
911    This constraint suspends until its first two arguments are ground.
912    It then unifies Bool according to the truth value of the corresponding
913    #::/2 constraint.
914<P>
915    Note: as opposed to #::/2, the first argument cannot be a list.
916    ")]
917).
918
919:- comment(($::)/3, [
920    summary: "Reified pure range constraint",
921    args: ["Vars":  "Variable or number, or a list of variables or numbers",
922           "Range": "Variable or Lo..Hi, where Lo and Hi are variables or numeric expressions",
923           "Bool": "Variable, 0 or 1"
924          ],
925    see_also:[($::)/2],
926    eg:"
927    ?- $::(X, 1 .. 5, B), X = 3.0.
928    B = 1
929    X = 3.0
930    Yes (0.00s cpu)
931
932    % range violated
933    ?- $::(X, 1 .. 5, B), X = 6.
934    B = 0
935    X = 6
936    Yes (0.00s cpu)
937    ",
938    desc: html("\
939    Reified version of $::/2, i.e. the truth value of the range constraint is
940    reflected in the value of the 0/1 variable Bool.
941<P>
942    This constraint suspends until its first two arguments are ground.
943    It then unifies Bool according to the truth value of the corresponding
944    $::/2 constraint.
945<P>
946    Note: as opposed to $::/2, the first argument cannot be a list.
947    ")]
948).
949
950:- comment(integers/1, [
951    summary: "Constrain Vars to be integers",
952    args: ["Vars": "List of variables or integers"],
953    see_also:[(::)/2, (#::)/2, reals/1],
954    fail_if: "Vars contains non-integers.",
955    desc: html("\
956    This constraint suspends until its argument is ground. It then succeeds
957    iff Vars is an integer or a list of integers.")]
958).
959
960:- comment(reals/1, [
961    summary: "Constrain Vars to be a number or list of numbers",
962    args: ["Vars": "List of variables or numbers"],
963    see_also:[(::)/2, ($::)/2, integers/1],
964    fail_if: "Vars contains non-numbers.",
965    eg:"
966    ?- reals(L), L = [3.4, 7].
967    L = [3.4, 7]
968    Yes (0.00s cpu)
969
970    ",
971    desc: html("\
972    This constraint suspends until its argument is ground. It then succeeds
973    iff Vars is a number or a list of numbers (any type).")]
974).
975
976:- comment((=:=)/2, [
977	summary: "The value of Expr1 is equal to the value of Expr2.",
978        template: "?Expr1 =:= ?Expr2",
979	see_also:[(=:=)/3, _:(=:=)/2],
980	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
981	fail_if:"   fails if the value of Expr1 is not equal to the value of Expr2",
982	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
983
984        desc: html("\
985   Suspends until both Expr1 and Expr2 are ground, and then both arguments
986   are evaluated and compared, succeeding iff they are equal (beware of
987   rounding errors when comparing reals).
988")
989]).
990
991:- comment((=\=)/2, [
992	summary: "The value of Expr1 is not equal to the value of Expr2.",
993        template: "?Expr1 =\\= ?Expr2",
994	see_also:[(=\=)/3, _:(=\=)/2],
995	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
996	fail_if:"   fails if the value of Expr1 is equal to the value of Expr2",
997	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
998
999        desc: html("\
1000   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1001   are evaluated and compared, succeeding iff they are not equal (beware of
1002   rounding errors when comparing reals).
1003")
1004]).
1005
1006:- comment((>=)/2, [
1007	summary: "The value of Expr1 is greater than or equal to the value of Expr2.",
1008        template: "?Expr1 >= ?Expr2",
1009	see_also:[(>=)/3, _:(>=)/2],
1010	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
1011	fail_if:"   fails if the value of Expr1 is smaller than the value of Expr2",
1012	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1013
1014        desc: html("\
1015   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1016   are evaluated and compared, succeeding iff Expr1 is greater than or equal to
1017   Expr2 (beware of rounding errors when comparing reals).
1018")
1019]).
1020
1021:- comment((=<)/2, [
1022	summary: "The value of Expr1 is less than or equal to the value of Expr2.",
1023        template: "?Expr1 =< ?Expr2",
1024	see_also:[(=<)/3, _:(=<)/2],
1025	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
1026	fail_if:"   fails if the value of Expr1 is greater than the value of Expr2",
1027	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1028
1029        desc: html("\
1030   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1031   are evaluated and compared, succeeding iff Expr1 is less than or equal to
1032   Expr2 (beware of rounding errors when comparing reals).
1033")
1034]).
1035
1036:- comment((>)/2, [
1037	summary: "The value of Expr1 is greater than the value of Expr2.",
1038        template: "?Expr1 > ?Expr2",
1039	see_also:[(>)/3, _:(>)/2],
1040	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
1041	fail_if:"   fails if the value of Expr1 is not greater than the value of Expr2",
1042	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1043
1044        desc: html("\
1045   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1046   are evaluated and compared, succeeding iff Expr1 is greater than Expr2
1047   (beware of rounding errors when comparing reals).
1048")
1049]).
1050
1051:- comment((<)/2, [
1052	summary: "The value of Expr1 is less than the value of Expr2.",
1053        template: "?Expr1 < ?Expr2",
1054	see_also:[(<)/3, _:(<)/2],
1055	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
1056	fail_if:"   fails if the value of Expr1 is not less than the value of Expr2",
1057	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1058
1059        desc: html("\
1060   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1061   are evaluated and compared, succeeding iff Expr1 is less than Expr2
1062   (beware of rounding errors when comparing reals).
1063")
1064]).
1065
1066:- comment(($=)/2, [
1067	summary: "The value of Expr1 is equal to the value of Expr2.",
1068        template: "?Expr1 $= ?Expr2",
1069	see_also:[($=)/3, suspend:(=:=)/2, _:($=)/2],
1070	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
1071	fail_if:"   fails if the value of Expr1 is not equal to the value of Expr2",
1072	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1073
1074        desc: html("\
1075   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1076   are evaluated and compared, succeeding iff they are equal (beware of
1077   rounding errors when comparing reals).
1078")
1079]).
1080
1081:- comment(($\=)/2, [
1082	summary: "The value of Expr1 is not equal to the value of Expr2.",
1083        template: "?Expr1 $\\= ?Expr2",
1084	see_also:[($\=)/3, suspend:(=\=)/2, _:($\=)/2],
1085	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
1086	fail_if:"   fails if the value of Expr1 is equal to the value of Expr2",
1087	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1088
1089        desc: html("\
1090   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1091   are evaluated and compared, succeeding iff they are not equal (beware of
1092   rounding errors when comparing reals).
1093")
1094]).
1095
1096:- comment(($>=)/2, [
1097	summary: "The value of Expr1 is greater than or equal to the value of Expr2.",
1098        template: "?Expr1 $>= ?Expr2",
1099	see_also:[($>=)/3, suspend:(>=)/2, _:($>=)/2],
1100	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
1101	fail_if:"   fails if the value of Expr1 is smaller than the value of Expr2",
1102	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1103
1104        desc: html("\
1105   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1106   are evaluated and compared, succeeding iff Expr1 is than greater or equal
1107   to Expr2 (beware of rounding errors when comparing reals).
1108")
1109]).
1110
1111:- comment(($=<)/2, [
1112	summary: "The value of Expr1 is less than or equal to the value of Expr2.",
1113        template: "?Expr1 $=< ?Expr2",
1114	see_also:[($=<)/3, suspend:(=<)/2, _:($=<)/2],
1115	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
1116	fail_if:"   fails if the value of Expr1 is greater than the value of Expr2",
1117	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1118
1119        desc: html("\
1120   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1121   are evaluated and compared, succeeding iff Expr1 is less than or equal to
1122   Expr2 (beware of rounding errors when comparing reals).
1123")
1124]).
1125
1126:- comment(($>)/2, [
1127	summary: "The value of Expr1 is greater than the value of Expr2.",
1128        template: "?Expr1 $> ?Expr2",
1129	see_also:[($>)/3, suspend:(>)/2, _:($>)/2],
1130	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
1131	fail_if:"   fails if the value of Expr1 is not greater than the value of Expr2",
1132	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1133
1134        desc: html("\
1135   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1136   are evaluated and compared, succeeding iff Expr1 is greater than Expr2
1137   (beware of rounding errors when comparing reals).
1138")
1139]).
1140
1141:- comment(($<)/2, [
1142	summary: "The value of Expr1 is less than the value of Expr2.",
1143        template: "?Expr1 $< ?Expr2",
1144	see_also:[($<)/3, suspend:(<)/2, _:($<)/2],
1145	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression"],
1146	fail_if:"   fails if the value of Expr1 is not less than the value of Expr2",
1147	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1148
1149        desc: html("\
1150   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1151   are evaluated and compared, succeeding iff Expr1 is less than Expr2
1152   (beware of rounding errors when comparing reals).
1153")
1154]).
1155
1156:- comment((#=)/2, [
1157	summary: "The integer value of Expr1 is equal to the integer value of Expr2.",
1158        template: "?Expr1 #= ?Expr2",
1159	see_also:[(#=)/3, _:(#=)/2],
1160	args:["Expr1" : "An integer arithmetic expression",
1161              "Expr2" : "An integer arithmetic expression"],
1162	fail_if:"   fails if the value of Expr1 is not equal to the value of Expr2, or if either do not evaluate to an integer.",
1163	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1164
1165        desc: html("\
1166   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1167   are evaluated and compared, succeeding iff they are both integers and are
1168   equal.
1169")
1170]).
1171
1172:- comment((#\=)/2, [
1173	summary: "The integer value of Expr1 is not equal to the integer value of Expr2.",
1174        template: "?Expr1 #\\= ?Expr2",
1175	see_also:[(#\=)/3, _:(#\=)/2],
1176	args:["Expr1" : "An integer arithmetic expression",
1177              "Expr2" : "An integer arithmetic expression"],
1178	fail_if:"   fails if the value of Expr1 is equal to the value of Expr2, or if either do not evaluate to an integer.",
1179	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1180
1181        desc: html("\
1182   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1183   are evaluated and compared, succeeding iff they are both integers and are
1184   not equal.
1185")
1186]).
1187
1188:- comment((#>=)/2, [
1189	summary: "The integer value of Expr1 is greater than or equal to the integer value of Expr2.",
1190        template: "?Expr1 #>= ?Expr2",
1191	see_also:[(#>=)/3, _:(#>=)/2],
1192	args:["Expr1" : "An integer arithmetic expression",
1193              "Expr2" : "An integer arithmetic expression"],
1194	fail_if:"   fails if the value of Expr1 is less than the value of Expr2, or if either do not evaluate to an integer.",
1195	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1196
1197        desc: html("\
1198   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1199   are evaluated and compared, succeeding iff they are both integers and
1200   Expr1 is greater than or equal to Expr2.
1201")
1202]).
1203
1204:- comment((#=<)/2, [
1205	summary: "The integer value of Expr1 is less than or equal to the integer value of Expr2.",
1206        template: "?Expr1 #=< ?Expr2",
1207	see_also:[(#=<)/3, _:(#=<)/2],
1208	args:["Expr1" : "An integer arithmetic expression",
1209              "Expr2" : "An integer arithmetic expression"],
1210	fail_if:"   fails if the value of Expr1 is greater than the value of Expr2, or if either do not evaluate to an integer.",
1211	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1212
1213        desc: html("\
1214   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1215   are evaluated and compared, succeeding iff they are both integers and
1216   Expr1 is less than or equal to Expr2.
1217")
1218]).
1219
1220:- comment((#>)/2, [
1221	summary: "The integer value of Expr1 is greater than the integer value of Expr2.",
1222        template: "?Expr1 #> ?Expr2",
1223	see_also:[(#>)/3, _:(#>)/2],
1224	args:["Expr1" : "An integer arithmetic expression",
1225              "Expr2" : "An integer arithmetic expression"],
1226	fail_if:"   fails if the value of Expr1 is not greater than the value of Expr2, or if either do not evaluate to an integer.",
1227	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1228
1229        desc: html("\
1230   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1231   are evaluated and compared, succeeding iff they are both integers and
1232   Expr1 is greater than Expr2.
1233")
1234]).
1235
1236:- comment((#<)/2, [
1237	summary: "The integer value of Expr1 is less than the integer value of Expr2.",
1238        template: "?Expr1 #< ?Expr2",
1239	see_also:[(#<)/3, _:(#<)/2],
1240	args:["Expr1" : "An integer arithmetic expression",
1241              "Expr2" : "An integer arithmetic expression"],
1242	fail_if:"   fails if the value of Expr1 is not less than the value of Expr2, or if either do not evaluate to an integer.",
1243	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1244
1245        desc: html("\
1246   Suspends until both Expr1 and Expr2 are ground, and then both arguments
1247   are evaluated and compared, succeeding iff they are both integers and
1248   Expr1 is less than Expr2.
1249")
1250]).
1251
1252:- comment((=:=)/3, [
1253	summary: "Reified arithmetic comparison",
1254	see_also:[(=:=)/2,($=)/3],
1255	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1256	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1257        desc: html("\
1258    Reified version of =:=/2, i.e. the truth value of the comparison is
1259    reflected in the value of the 0/1 variable Bool.
1260<P>
1261    This constraint suspends until its first two arguments are ground.
1262    It then unifies Bool according to the truth value of the corresponding
1263    =:=/2 constraint.
1264")
1265]).
1266
1267:- comment(($=)/3, [
1268	summary: "Reified arithmetic comparison",
1269	see_also:[($=)/2,(=:=)/3],
1270	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1271	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1272        desc: html("\
1273    Reified version of $=/2, i.e. the truth value of the comparison is
1274    reflected in the value of the 0/1 variable Bool.
1275<P>
1276    This constraint suspends until its first two arguments are ground.
1277    It then unifies Bool according to the truth value of the corresponding
1278    $=/2 constraint.
1279")
1280]).
1281
1282:- comment((#=)/3, [
1283	summary: "Reified arithmetic comparison",
1284	see_also:[(#=)/2],
1285	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1286	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1287        desc: html("\
1288    Reified version of #=/2, i.e. the truth value of the comparison is
1289    reflected in the value of the 0/1 variable Bool.
1290<P>
1291    This constraint suspends until its first two arguments are ground.
1292    It then unifies Bool according to the truth value of the corresponding
1293    #=/2 constraint.
1294")
1295]).
1296
1297:- comment((=\=)/3, [
1298	summary: "Reified arithmetic comparison",
1299	see_also:[(=\=)/2,($\=)/3],
1300	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1301	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1302        desc: html("\
1303    Reified version of =\\=/2, i.e. the truth value of the comparison is
1304    reflected in the value of the 0/1 variable Bool.
1305<P>
1306    This constraint suspends until its first two arguments are ground.
1307    It then unifies Bool according to the truth value of the corresponding
1308    =\\=/2 constraint.
1309")
1310]).
1311
1312:- comment(($\=)/3, [
1313	summary: "Reified arithmetic comparison",
1314	see_also:[($\=)/2,(=\=)/3],
1315	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1316	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1317        desc: html("\
1318    Reified version of $\\=/2, i.e. the truth value of the comparison is
1319    reflected in the value of the 0/1 variable Bool.
1320<P>
1321    This constraint suspends until its first two arguments are ground.
1322    It then unifies Bool according to the truth value of the corresponding
1323    $\\=/2 constraint.
1324")
1325]).
1326
1327:- comment((#\=)/3, [
1328	summary: "Reified arithmetic comparison",
1329	see_also:[(#\=)/2],
1330	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1331	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1332        desc: html("\
1333    Reified version of #\\=/2, i.e. the truth value of the comparison is
1334    reflected in the value of the 0/1 variable Bool.
1335<P>
1336    This constraint suspends until its first two arguments are ground.
1337    It then unifies Bool according to the truth value of the corresponding
1338    #\\=/2 constraint.
1339")
1340]).
1341
1342:- comment((>=)/3, [
1343	summary: "Reified arithmetic comparison",
1344	see_also:[(>=)/2,($>=)/3],
1345	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1346	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1347        desc: html("\
1348    Reified version of >=/2, i.e. the truth value of the comparison is
1349    reflected in the value of the 0/1 variable Bool.
1350<P>
1351    This constraint suspends until its first two arguments are ground.
1352    It then unifies Bool according to the truth value of the corresponding
1353    >=/2 constraint.
1354")
1355]).
1356
1357:- comment(($>=)/3, [
1358	summary: "Reified arithmetic comparison",
1359	see_also:[($>=)/2,(>=)/3],
1360	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1361	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1362        desc: html("\
1363    Reified version of $>=/2, i.e. the truth value of the comparison is
1364    reflected in the value of the 0/1 variable Bool.
1365<P>
1366    This constraint suspends until its first two arguments are ground.
1367    It then unifies Bool according to the truth value of the corresponding
1368    $>=/2 constraint.
1369")
1370]).
1371
1372:- comment((#>=)/3, [
1373	summary: "Reified arithmetic comparison",
1374	see_also:[(#>=)/2],
1375	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1376	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1377        desc: html("\
1378    Reified version of #>=/2, i.e. the truth value of the comparison is
1379    reflected in the value of the 0/1 variable Bool.
1380<P>
1381    This constraint suspends until its first two arguments are ground.
1382    It then unifies Bool according to the truth value of the corresponding
1383    #>=/2 constraint.
1384")
1385]).
1386
1387:- comment((=<)/3, [
1388	summary: "Reified arithmetic comparison",
1389	see_also:[(=<)/2,($=<)/3],
1390	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1391	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1392        desc: html("\
1393    Reified version of =</2, i.e. the truth value of the comparison is
1394    reflected in the value of the 0/1 variable Bool.
1395<P>
1396    This constraint suspends until its first two arguments are ground.
1397    It then unifies Bool according to the truth value of the corresponding
1398    =</2 constraint.
1399")
1400]).
1401
1402:- comment(($=<)/3, [
1403	summary: "Reified arithmetic comparison",
1404	see_also:[($=<)/2,(=<)/3],
1405	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1406	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1407        desc: html("\
1408    Reified version of $=</2, i.e. the truth value of the comparison is
1409    reflected in the value of the 0/1 variable Bool.
1410<P>
1411    This constraint suspends until its first two arguments are ground.
1412    It then unifies Bool according to the truth value of the corresponding
1413    $=</2 constraint.
1414")
1415]).
1416
1417:- comment((#=<)/3, [
1418	summary: "Reified arithmetic comparison",
1419	see_also:[(#=<)/2],
1420	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1421	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1422        desc: html("\
1423    Reified version of #=</2, i.e. the truth value of the comparison is
1424    reflected in the value of the 0/1 variable Bool.
1425<P>
1426    This constraint suspends until its first two arguments are ground.
1427    It then unifies Bool according to the truth value of the corresponding
1428    #=</2 constraint.
1429")
1430]).
1431
1432:- comment((>)/3, [
1433	summary: "Reified arithmetic comparison",
1434	see_also:[(>)/2,($>)/3],
1435	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1436	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1437        desc: html("\
1438    Reified version of >/2, i.e. the truth value of the comparison is
1439    reflected in the value of the 0/1 variable Bool.
1440<P>
1441    This constraint suspends until its first two arguments are ground.
1442    It then unifies Bool according to the truth value of the corresponding
1443    >/2 constraint.
1444")
1445]).
1446
1447:- comment(($>)/3, [
1448	summary: "Reified arithmetic comparison",
1449	see_also:[($>)/2,(>)/3],
1450	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1451	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1452        desc: html("\
1453    Reified version of $>/2, i.e. the truth value of the comparison is
1454    reflected in the value of the 0/1 variable Bool.
1455<P>
1456    This constraint suspends until its first two arguments are ground.
1457    It then unifies Bool according to the truth value of the corresponding
1458    $>/2 constraint.
1459")
1460]).
1461
1462:- comment((#>)/3, [
1463	summary: "Reified arithmetic comparison",
1464	see_also:[(#>)/2],
1465	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1466	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1467        desc: html("\
1468    Reified version of #>/2, i.e. the truth value of the comparison is
1469    reflected in the value of the 0/1 variable Bool.
1470<P>
1471    This constraint suspends until its first two arguments are ground.
1472    It then unifies Bool according to the truth value of the corresponding
1473    #>/2 constraint.
1474")
1475]).
1476
1477:- comment((<)/3, [
1478	summary: "Reified arithmetic comparison",
1479	see_also:[(<)/2,($<)/3],
1480	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1481	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1482        desc: html("\
1483    Reified version of </2, i.e. the truth value of the comparison is
1484    reflected in the value of the 0/1 variable Bool.
1485<P>
1486    This constraint suspends until its first two arguments are ground.
1487    It then unifies Bool according to the truth value of the corresponding
1488    </2 constraint.
1489")
1490]).
1491
1492:- comment(($<)/3, [
1493	summary: "Reified arithmetic comparison",
1494	see_also:[($<)/2,(<)/3],
1495	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1496	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1497        desc: html("\
1498    Reified version of $</2, i.e. the truth value of the comparison is
1499    reflected in the value of the 0/1 variable Bool.
1500<P>
1501    This constraint suspends until its first two arguments are ground.
1502    It then unifies Bool according to the truth value of the corresponding
1503    $</2 constraint.
1504")
1505]).
1506
1507:- comment((#<)/3, [
1508	summary: "Reified arithmetic comparison",
1509	see_also:[(#<)/2],
1510	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1511	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1512        desc: html("\
1513    Reified version of #</2, i.e. the truth value of the comparison is
1514    reflected in the value of the 0/1 variable Bool.
1515<P>
1516    This constraint suspends until its first two arguments are ground.
1517    It then unifies Bool according to the truth value of the corresponding
1518    #</2 constraint.
1519")
1520]).
1521
1522
1523:- comment((and)/2, [
1524	summary: "Both Expr1 and Expr2 arithmetically evaluate to 1",
1525        template: "?Expr1 and ?Expr2",
1526	see_also:[(and)/3, (or)/2, (=>)/2, (neg)/1, _:(and)/2],
1527	args:["Expr1" : "A boolean expression",
1528              "Expr2" : "A boolean expression"],
1529	fail_if:"Expr1 or Expr2 do not both evaluate to 1",
1530	eg:"
1531	?- B and 1, B = 1.
1532	B = 1
1533	Yes (0.00s cpu)
1534
1535	?- B and 1, B = 0.
1536	No (0.00s cpu)
1537
1538	% arguments are typically reifiable expressions:
1539	?- X > 5 and X < 7, X = 7.
1540	No (0.00s cpu)
1541
1542	% the previous example is equivalent to:
1543	?- >(X,5,B1), <(X,7,B2), B1 and B2, X=7.
1544	No (0.00s cpu)
1545
1546	% and/or/=>/neg are themselves reifiable:
1547	?- X > 5 and neg(X < 7), X = 7.
1548	X = 7
1549	Yes (0.00s cpu)
1550	",
1551	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1552        desc: html("\
1553    Suspends until both Expr1 and Expr2 are ground, and then both arguments
1554    are evaluated. Succeeds if both evaluate to 1.
1555<P>
1556    Typically, the expressions contains reifiable constraints, in which case
1557    a corresponding reified constraint is set up, and the expression is
1558    replaced by the resulting boolean variable.
1559")
1560]).
1561
1562:- comment((or)/2, [
1563	summary: "At least one of Expr1 or Expr2 arithmetically evaluate to 1",
1564        template: "?Expr1 or ?Expr2",
1565	see_also:[(or)/3, (and)/2, (=>)/2, (neg)/1, _:(or)/2],
1566	args:["Expr1" : "A boolean expression",
1567              "Expr2" : "A boolean expression"],
1568	fail_if:"Neither Expr1 nor Expr2 evaluates to 1",
1569	eg:"
1570	?- B or 1, B = 0.
1571	B = 0
1572	Yes (0.00s cpu)
1573
1574	?- B or 0, B = 0.
1575	No (0.00s cpu)
1576
1577	% arguments are typically reifiable expressions:
1578	?- X > 7 or X < 5, X = 7.
1579	No (0.00s cpu)
1580
1581	% the previous example is equivalent to:
1582	?- >(X,7,B1), <(X,5,B2), B1 or B2, X=7.
1583	No (0.00s cpu)
1584
1585	% and/or/=>/neg are themselves reifiable:
1586	?- X > 7 or neg(X < 5), X = 7.
1587	X = 7
1588	Yes (0.00s cpu)
1589	",
1590	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1591        desc: html("\
1592    Suspends until both Expr1 and Expr2 are ground, and then both arguments
1593    are evaluated. Succeeds if at least one evaluates to 1.
1594<P>
1595    Typically, the expressions contains reifiable constraints, in which case
1596    a corresponding reified constraint is set up, and the expression is
1597    replaced by the resulting boolean variable.
1598")
1599]).
1600
1601:- comment((=>)/2, [
1602	summary: "If Expr1 arithmetically evaluates to 1, so does Expr2 (implication)",
1603        template: "?Expr1 => ?Expr2",
1604	see_also:[(=>)/3, (and)/2, (or)/2, (neg)/1, _:(=>)/2],
1605	args:["Expr1" : "A boolean expression",
1606              "Expr2" : "A boolean expression"],
1607	fail_if:"Expr1 evaluates to 1 and Expr2 evaluates to 0",
1608	eg:"
1609	?- 0 => B, B = 0.
1610	B = 0
1611	Yes (0.00s cpu)
1612
1613	?- 0 => B, B = 1.
1614	B = 1
1615	Yes (0.00s cpu)
1616
1617	?- 1 => B, B = 0.
1618	No (0.00s cpu)
1619
1620	?- 1 => B, B = 1.
1621	B = 1
1622	Yes (0.00s cpu)
1623
1624	% arguments are typically reifiable expressions:
1625	?- X > Y => X > Y+10, X = 5, Y = 3.
1626	No (0.00s cpu)
1627
1628	% the previous example is equivalent to:
1629	?- >(X,Y,B1), >(X,Y+10,B2), B1 => B2, X = 5, Y = 3.
1630	No (0.00s cpu)
1631
1632	% and/or/=>/neg are themselves reifiable:
1633	?- neg(A => B) or (C => D), A=1, B=0, C=0, D=1.
1634	A = 1
1635	B = 0
1636	C = 0
1637	D = 1
1638	Yes (0.00s cpu)
1639	",
1640	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1641        desc: html("\
1642    Suspends until both Expr1 and Expr2 are ground, and then both arguments
1643    are evaluated. Succeeds if the truth of Expr1 implies Expr2, i.e. if Expr1
1644    evaluates to 1, Expr2 must evaluate to 1, otherwise Expr2 can evaluate to
1645    1 or 0. Logically equivalent to
1646    <PRE>
1647    	neg(Expr1) or Expr2.
1648    </PRE>
1649    Typically, the expressions contains reifiable constraints, in which case
1650    a corresponding reified constraint is set up, and the expression is
1651    replaced by the resulting boolean variable.
1652")
1653]).
1654
1655:- comment((neg)/1, [
1656	summary: "Expr arithmetically evaluates to 0",
1657        template: "neg ?Expr",
1658	see_also:[(neg)/2, (and)/2, (or)/2, (=>)/2, _:(neg)/1],
1659	args:["Expr" : "A boolean expression"],
1660	fail_if:"Expr does not evaluate to 0",
1661	eg:"
1662	?- neg B, B = 1.
1663	No (0.00s cpu)
1664
1665	?- neg B, B = 0.
1666	B = 0
1667	Yes (0.00s cpu)
1668
1669	% arguments are typically reifiable expressions:
1670	?- neg X > 7, X = 8.
1671	No (0.00s cpu)
1672
1673	% the previous example is equivalent to:
1674	?- >(X,7,B), neg B, X=8.
1675	No (0.00s cpu)
1676
1677	% and/or/=>/neg are themselves reifiable:
1678	?- neg(X > 7 or X < 5), X = 7.
1679	X = 7
1680	Yes (0.00s cpu)
1681	",
1682	exceptions:[24 : "Expr is not an arithmetic expression."],
1683        desc: html("\
1684    Suspends until Expr is ground, and then evaluates it. Succeeds if it
1685    evaluates to 0.
1686<P>
1687    Typically, the expression contains reifiable constraints, in which case
1688    a corresponding reified constraint is set up, and the expression is
1689    replaced by the resulting boolean variable.
1690")
1691]).
1692
1693:- comment((and)/3, [
1694	summary: "Reified boolean operation",
1695	see_also:[(and)/2],
1696	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1697	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1698        desc: html("\
1699    Reified version of and/2, i.e. the truth value of the boolean operation
1700    is reflected in the value of the 0/1 variable Bool.
1701<P>
1702    This constraint suspends until its first two arguments are ground.
1703    It then unifies Bool according to the truth value of the corresponding
1704    and/2 constraint.
1705")
1706]).
1707
1708:- comment((or)/3, [
1709	summary: "Reified boolean operation",
1710	see_also:[(or)/2],
1711	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1712	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1713        desc: html("\
1714    Reified version of or/2, i.e. the truth value of the boolean operation
1715    is reflected in the value of the 0/1 variable Bool.
1716<P>
1717    This constraint suspends until its first two arguments are ground.
1718    It then unifies Bool according to the truth value of the corresponding
1719    or/2 constraint.
1720")
1721]).
1722
1723:- comment((=>)/3, [
1724	summary: "Reified boolean operation",
1725	see_also:[(=>)/2],
1726	args:["Expr1" : "An arithmetic expression", "Expr2" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1727	exceptions:[24 : "Expr1 or Expr2 is not an arithmetic expression."],
1728        desc: html("\
1729    Reified version of =>/2, i.e. the truth value of the boolean operation
1730    is reflected in the value of the 0/1 variable Bool.
1731<P>
1732    This constraint suspends until its first two arguments are ground.
1733    It then unifies Bool according to the truth value of the corresponding
1734    =>/2 constraint.
1735")
1736]).
1737
1738:- comment((neg)/2, [
1739	summary: "Reified boolean operation",
1740	see_also:[(neg)/1],
1741	args:["Expr" : "An arithmetic expression", "Bool":"Variable, 0 or 1"],
1742	exceptions:[24 : "Expr is not an arithmetic expression."],
1743        desc: html("\
1744    Reified version of neg/1, i.e. the truth value of the boolean operation
1745    is reflected in the value of the 0/1 variable Bool.
1746<P>
1747    This constraint suspends until its first argument is ground.
1748    It then unifies Bool according to the truth value of the corresponding
1749    neg/1 constraint.
1750")
1751]).
1752
1753