Personal tools
Document Actions

press-interval-package-2.pl.txt

by Paul McJones last modified 2023-03-15 16:24

Click here to get the file

Size 20.3 kB - File type text/plain

File contents

/* INT : Finds intervals of terms in PRESS

						Alan Bundy
						Updated: 30 September 82

	Alan Bundy 19.12.79, revised 13.3.80, 26.3.81
	Cosmetics by Lawrence 18.6.81, later versions
	new pi, e handling by R.A.O'K 30.9.82
*/

/* EXPORT */

:- public
	vet/2,
	positive/1,
	negative/1,
	non_neg/1,
	non_pos/1,
	non_zero/1,
	acute/1,
	obtuse/1,
	non_reflex/1,
	
	less_than/2,			% Used in a \+
	
	find_int/2,			% Exported for convenience
	int_apply/3.


/* IMPORT */
/*
	error/3				from  UTIL:TRACE
	
	memberchk/2			from  UTIL:SETROU
	
	number/1			from  LONG
	eval/1
	eval/2
	
	measure/2			from  notional Mecho database
	quantity/1
	angle/3
	incline/3
	concavity/2
	slope/2
	partition/2
	
%%	special_atom/1  		from  ARITH:FACTS  not needed!
*/


/* MODES */

:- mode
	vet(+,?),
	positive(+),
	negative(+),
	non_neg(+),
	non_pos(+),
	non_zero(+),
	acute(+),
	obtuse(+),
	non_reflex(+),
 
	    gen_combine(+,?),
	    combine(+,+,?),
	    in(+,+),
	    sub_int(+,+),
	    below(+,+),
	    disjoint(+,+),
	    overlap(+,+),
	    marker_flip(?,?),
 
		default_interval(?),
	    find_int(+,?),
		find_int2(+,-),
		find_int_args(+,-,-),
		find_simple_int(+,-),
		    make_assumption_positive(+),
 
	    int_apply(+,+,-),
		int_apply_all(+,+,-),
		all_are_contained(+,+),
		make_regions(+,+,-),
		    split(+,+,+,-),
			split1(+,+,-),
		    cartesian_product(+,+,-,?),
			cart_prod(+,+,+,-,?),
		find_limits(+,+,+,-),
		    clean_up(+,-),
		    limits(+,+,+,+,?),
		    get_bnds(+,+,+,-),
			updown_flip(+,+,-),
			get_bnd(+,+,-),
 
	    order(+,+,?,?),
	    less_than(+,+),
	    calc(+,+,?),
		breakup_bnds(+,-,-),
	    comb(+,?),
 
	    mono(+,?,?),
 
	    classify(+,-),
		interval(+,-,-),
		collect_intervals(+,+,-),
		quad(+,+,+,?).
 

/*
    Data structures

	<interval>	has form	i(LMarker,Bottom,Top,RMarker)
	<boundary>	has form	b(N,Marker)

		where:
			Bottom, Top, N		  are  <numbers>
			LMarker, RMarker, Marker  are  one of {open,closed}

	An interval ranges between Bottom and Top and is open or closed at
	the ends depending on LMarker (for Bottom) and RMarker (for Top).

	A boundary is an end of an interval. There are operations defined
	over these boundaries which are then used to help define the
	operations over intervals. Note that the notion of a boundary does
	NOT involve any specific end of an interval (ie Top/Bottom). They
	are a generalisation over all such ends.

*/

%% @@@ - marker (top of code)


/****************************************/
/* Use interval information - top level */
/****************************************/

			% Check that solution is admissible

vet(true,true).

vet(false,false).

vet(A&B,A1&B1) :- vet(A,A1), vet(B,B1).

vet(A#B,A1#B1) :- vet(A,A1), vet(B,B1).

vet(A=B,A=B) :- 
	find_int(A,IntA), find_int(B,IntB),
	overlap(IntA,IntB),
	!.

vet(A=B,false).

	
			% X is positive, negative, acute, etc.

positive(X) :- find_int(X,i(L,B,T,R)), less_than(b(0,closed),b(B,L)).

negative(X) :- find_int(X,i(L,B,T,R)), less_than(b(T,R),b(0,closed)).

non_neg(X) :- find_int(X,i(L,B,T,R)), less_than(b(0,open),b(B,L)).

non_pos(X) :- find_int(X,i(L,B,T,R)), less_than(b(T,R),b(0,open)).

non_zero(X^N) :- !, non_zero(X).	%ad hoc patch (replaces negative(N)

non_zero(X) :-
	find_int(X,i(L,B,T,R)),
	( less_than(b(0,closed),b(B,L)) ; less_than(b(T,R),b(0,closed)) ),
	!.

acute(X) :-
	find_int(X,i(L,B,T,R)),
	less_than(b(0,open),b(B,L)),
	less_than(b(T,R),b(90,open)).

obtuse(X) :-
	find_int(X,i(L,B,T,R)),
	less_than(b(90,open),b(B,L)),
	less_than(b(T,R),b(180,open)).

non_reflex(X) :-
	find_int(X,i(L,B,T,R)),
	less_than(b(0,open),b(B,L)),
	less_than(b(T,R),b(180,open)).



/*****************************************/
/*	Manipulating Intervals		 */
/*****************************************/


			% Combine a list of intervals by sweeping list and
			%  accumulating the combined intervals.

gen_combine([FirstInt|RestInts],Result)
     :-	gen_combine(RestInts,FirstInt,Result).


gen_combine([],Result,Result).

gen_combine([Int|RestInts],Acc,Result)
     :-	combine(Int,Acc,NewAcc),
	gen_combine(RestInts,NewAcc,Result).


			% Combine x and y intervals

combine(i(Lx,Bx,Tx,Rx), i(Ly,By,Ty,Ry), i(L,B,T,R)) :-
	order(b(Tx,Rx),b(Ty,Ry),_,b(T,R)),
	order(b(Bx,Lx),b(By,Ly),b(B,L),_).


			% Number N is contained in interval

in(N,i(L,B,T,R)) :- !,
	sub_int(i(closed,N,N,closed),i(L,B,T,R)).


			% x interval is contained in second interval

sub_int(i(Lx,Bx,Tx,Rx),i(L,B,T,R)) :-
	marker_flip(L,L1), marker_flip(R,R1),
	less_than(b(B,L1),b(Bx,Lx)), less_than(b(Tx,Rx),b(T,R1)).

			% x interval is wholly below y interval

below(i(Lx,Bx,Tx,Rx),i(Ly,By,Ty,Ry)) :-
	less_than(b(Tx,Rx),b(By,Ly)), !.

			% x and y intervals are disjoint

disjoint(IntX,IntY) :- below(IntX,IntY), !.
disjoint(IntX,IntY) :- below(IntY,IntX), !.

			% x and y intervals overlap

%% overlap(IntX,IntY) :- \+ disjoint(IntX,IntY).

overlap(IntX,IntY) :- disjoint(IntX,IntY), !, fail.
overlap(_,_).


			% open and closed are opposites
			%  (this is how to flip them)

marker_flip(open,closed) :- !.
marker_flip(closed,open).



/****************************************/
/* X lies in closed or open interval    */
/****************************************/


		% Worst case default for intervals

default_interval(i(open,neginfinity,infinity,open)).


		% Let's try to do better.

find_int(X,Interval)
     :-	find_int2(X,Result),		% guarantee mode (+,-)
	Interval = Result.


			% Catch variables (shouldn't be there!)

find_int2(V,_)
     :-	var(V),
	!,
	error('Interval package given variable: %w',[V],fail).

			% Base cases
			%  Numbers have point intervals
			%  Symbols (atoms) have various special cases

find_int2(X,i(closed,X,X,closed)) :- number(X), !.

find_int2(X,Interval) :- atom(X), !, find_simple_int(X,Interval).


			% Special case normalisation

			% Convert ^(-1) to 1/

find_int2(X^(-1), Int) :- !,
	find_int2(1/X, Int).

			% Deal with exponentials to even power

find_int2(X^N, i(L,B,T,R)) :- 
	even(N), !,
	find_int(abs(X), i(Lx,Bx,Tx,Rx)),
	calc(^,[b(Bx,Lx),b(N,closed)],b(B,L)),
	calc(^,[b(Tx,Rx),b(N,closed)],b(T,R)).

			% Convert cosecant to sine

find_int2(csc(X), Int) :- !, find_int2(1/sin(X), Int).

			% Convert secant to cosine

find_int2(sec(X), Int) :- !, find_int2(1/cos(X), Int).

			% Convert cotangent to tangent

find_int2(cot(X), Int) :- !, find_int2(1/tan(X), Int).


			% General case
			%  Recursively find intervals for arguments and
			%  then int_apply to sort this out. This will use
			%  monotonicity of F to calculate interval of Term
			%  from arguments.

find_int2(Term,Int) :-
	find_int_args(Term,F,IntList),
	int_apply(F,IntList,Int),
	!.


			% If the general case fails

find_int2(sin(X), i(closed,(-1),1,closed)) :- !.
find_int2(cos(X), i(closed,(-1),1,closed)) :- !.

find_int2(X,Default) :- default_interval(Default).



			% Find a list of intervals corresponding to the
			%  arguments of Term. Also return the functor.

find_int_args(Term,Fn,IntList)
     :-	functor(Term,Fn,Arity),
	find_int_args(1,Arity,Term,IntList).


find_int_args(N,Max,_,[]) :- N > Max, !.

find_int_args(N,Max,Term,[Int|IntRest])
     :-	arg(N,Term,Arg),
	find_int2(Arg,Int),
	N1 is N+1,
	find_int_args(N1,Max,Term,IntRest).



			% Find the interval for a simple symbol
			%  This involves looking to see if we know
			%  anything special about the symbol which will
			%  help us.
			% Ad hoc patch for gravity - proper solution means
			%  allowing equations between quantities and defining
			%  g as measure(g,32,ft/sec^2).
			% pi and e have explicit conservative intervals
			% Otherwise try to classify symbol (if it is an angle)
			% Otherwise assume all quantities are positive
			%  	(possibly extreme?)
			% If there is no useful info we must use the default.

find_simple_int(g, i(open,1,infinity,open)) :- !.

find_simple_int(e, i(open,5/2,3,open)) :- !.

find_simple_int(pi,i(open,3,10/3,open)) :- !.

find_simple_int(X,Int) :- classify(X,Int), !.

find_simple_int(M,i(open,0,infinity,open)) :-
	measure(Q,M), quantity(Q),
	!,
	make_assumption_positive(M).

find_simple_int(X,Default) :- default_interval(Default).



			% Make and remember assumption

make_assumption_positive(X) :- assumed_positive(X), !.

make_assumption_positive(X)
     :-	assert( assumed_positive(X) ),
	trace('I assume %t positive.\n',[X],1).



/*************************************************************/
/* Find interval of function from intervals of its arguments */
/*************************************************************/


				% Simple case

int_apply(F,Region,Int) :-
	mono(F,Is,Mono),
	all_are_contained(Region,Is),
	!,
	find_limits(F,Region,Mono,Int).

				% Complex Case

int_apply(F,Region,Int) :-
	mono(F,MRegion,Mono),
	make_regions(Region,MRegion,NewRegions),
	int_apply_all(NewRegions,F,IntervalSet),
	!,
	gen_combine(IntervalSet,Int).



			% int_apply all intervals in a set (list)

int_apply_all([],_,[]).

int_apply_all([Region1|Rest],F,[Int1|IRest])
     :-	int_apply(F,Region1,Int1),
	int_apply_all(Rest,F,IRest).



			% All the argument intervals are sub intervals of
			%  the corresponding monotonic intervals for the
			%  function (from mono). (ie maplist sub_int down
			%  the two "argument" lists).

all_are_contained([],[]).

all_are_contained([ArgInt|ArgRest],[FInt|FRest])
     :-	sub_int(ArgInt,FInt),
	all_are_contained(ArgRest,FRest).



			% Given the list of actual intervals and the list
			%  of monotonic intervals for the function build
			%  a set of similar interval lists, derived from the
			%  actual interval list, but such that each element
			%  of each list in the set is wholly inside or outside
			%  its corresponding monotonic function interval.
			% This amounts to case splitting the actual interval
			%  list into a set of intervals for more tractable
			%  (sub) regions in the nD space.
			% Implemented by splitting lists to form a list of
			%  sets and taking the nD cartesian product. Note
			%  that both split/4 and cartesian_product/4 perform
			%  order reversals - which cancel each other out.

make_regions(Region,MRegion,NewRegions)
     :-	split(Region,MRegion,[],ListOfSets),
	cartesian_product(ListOfSets,[],NewRegions,[]).



			% Given the list of actual intervals and the list of
			%  monotonic intervals for the function, we build
			%  a list of n sets, where n is the arity of the 
			%  function (ie the length of the lists) and where
			%  each set contains intervals which are wholly inside
			%  or outside the corresponding monotonic function
			%  intervals, such that the intervals in each set
			%  would combine to form the corresponding actual
			%  interval.
			% The combining property follows from the way we split
			%  up the actual intervals.
			% The sets produced at the moment will only ever have
			%  number of members m such that: 1 =< m =< 3.
			%  The following special representations are used for
			%  these cases:
			%  		singleton(A)
			%  		pair(A,B)
			%  		triple(A,B,C)
			%  In fact the code will currently never produce sets
			%  of 3 elements (triples), but I (Lawrence) think
			%  this is probably a bug so have left the option, and
			%  this comment, around til we see.
			% Note that the list of sets built will be in reverse
			%  order compared with the "argument" lists. This is
			%  is implemented by an extra accumulator argument
			%  (should be [] to start with) onto which each Set
			%  is pushed.

split([],[],Result,Result).

split([ArgInt|ArgRest],[FInt|FRest],Sofar,Result)
     :-	split1(ArgInt,FInt,Set),
	split(ArgRest,FRest,[Set|Sofar],Result).


				% Intx wholly within Int

split1(Intx,Int,singleton(Intx)) :-
	sub_int(Intx,Int),
	!.

				% Intx and Int overlap with Intx leftmost

split1(i(Lx,Bx,Tx,Rx), i(L,B,T,R), pair(i(L,B,Tx,Rx),i(Lx,Bx,B1,L1)) ) :-
	marker_flip(R,R1), marker_flip(L,L1), 
	marker_flip(Lx,Lx1),
	correct(B,B1),
	less_than(b(Tx,Rx),b(T,R1)),
	\+ less_than(b(Tx,Rx),b(B,L)),
	less_than(b(Bx,Lx1),b(B,L)), !.



			% Given a list of n sets produce the a set of the
			%  elements from the nD cartesian product of the sets.
			%  The incoming sets are represented with special
			%  functors as there are only a few special cases (see
			%  split). The resulting product set is represented as
			%  a list. Each element will itself be a list (of n
			%  intervals) where the order of this element list will 
			%  be the reverse of the order in which the items
			%  were found in the original list of sets.
			% The implementation involves an accumulator for the
			%  (partial) element being built and uses the 
			%  difference list technique to build the final set
			%  of elements (repn as a list).

cartesian_product([],Element,[Element|Z],Z).

cartesian_product([First|Rest],PartialElement,ProductSet,Z)
     :-	cart_prod(First,Rest,PartialElement,ProductSet,Z).



cart_prod(singleton(A),Rest,PartialElement,PSet,Z)
     :-	cartesian_product(Rest,[A|PartialElement],PSet,Z).

cart_prod(pair(A,B),Rest,PartialElement,PSet0,Z)
     :-	cartesian_product(Rest,[A|PartialElement],PSet0,PSet1),
	cartesian_product(Rest,[B|PartialElement],PSet1,Z).

cart_prod(triple(A,B,C),Rest,PartialElement,PSet0,Z)
     :-	cartesian_product(Rest,[A|PartialElement],PSet0,PSet1),
	cartesian_product(Rest,[B|PartialElement],PSet1,PSet2),
	cartesian_product(Rest,[C|PartialElement],PSet2,Z).



			% Calculate Bottom and Top of Interval

find_limits(F,Region,Mono,Int) :-
	limits(bottom,F,Region,Mono,b(B,L)),
	limits(top,F,Region,Mono,b(T,R)), 
	clean_up(i(L,B,T,R), Int).


			% Hack to clear up various funnies

clean_up(i(_,undefined,_,_), Int) :- !, default_interval(Int).
clean_up(i(_,_,undefined,_), Int) :- !, default_interval(Int).
clean_up(i(L,B,0,R), i(L,B,-(0),R)) :- !.
clean_up(Int, Int).

correct(0,-(0)) :- !.
correct(B,B) :- !.


			% Calculate limit for a particular boundary

limits(TopBot,F,Region,Mono,Boundary)
     :-	get_bnds(Mono,TopBot,Region,BoundaryList),
	calc(F,BoundaryList,Boundary).



			% Form a boundary list from an interval list
			%  given various details - up+down x top+bottom.

get_bnds([],_,[],[]).

get_bnds([Mono|MRest],TopBot,[Int|IRest],[Bnd|BRest])
     :-	updown_flip(TopBot,Mono,NewMono),
	get_bnd(NewMono,Int,Bnd),
	get_bnds(MRest,TopBot,IRest,BRest).


	updown_flip(top,UD,UD).
	updown_flip(bottom,up,down) :- !.
	updown_flip(bottom,down,up).


	get_bnd(up,  i(L,B,T,R), b(T,R)).
	get_bnd(down,i(L,B,T,R), b(B,L)).




/*****************************************/
/*	Manipulating Boundaries		 */
/*****************************************/


			% Put boundaries in order

					% Boundaries are identical
order(Bnd,Bnd,Bnd,Bnd) :- !.
					% One of Mis is closed
order(b(N,M1),b(N,M2),b(N,closed),b(N,closed)) :- !.
					% Numbers are different, N1 smallest
order(b(N1,M1),b(N2,M2),b(N1,M1),b(N2,M2)) :-
	eval(N1 < N2), !.
					% N2 is smallest
order(b(N1,M1),b(N2,M2),b(N2,M2),b(N1,M1)).



			% Ordering of boundaries
			%  (assumes intervals are consecutive)

less_than(b(X,Mx),b(Y,My)) :- 
	comb([Mx,My],M),
	less_than_eval(M,X,Y).


less_than_eval(open,X,Y) :- eval( X =< Y ).

less_than_eval(closed,X,Y) :- eval( X < Y ).



			% Apply Function F to a boundary list
			%  Do this by combining the boundary markers and
			%  applying F to the numbers.

calc(F,BoundaryList,b(X,M)) :-
	breakup_bnds(BoundaryList,Markers,Numbers),
	comb(Markers,M),
	Term =.. [F|Numbers],
	eval(Term,X),
	!.


breakup_bnds([],[],[]).

breakup_bnds([b(N,M)|Rest],[M|MRest],[N|NRest])
     :-	breakup_bnds(Rest,MRest,NRest).



			% Combine boundary markers
			%  Result = open if any of the inputs is open

comb(MarkerList,Result) :- memberchk(open,MarkerList), !, Result = open.

comb(_,closed).



/**********************************************/
/* Monotonicity of Functions in each Interval */
/**********************************************/

/* unary minus */
mono(-, [i(closed,neginfinity,infinity,closed)], [down]).

/* addition */
mono(+,[i(closed,neginfinity,infinity,closed), 
	i(closed,neginfinity,infinity,closed)], [up,up]).

/* binary minus */
mono(-,[i(closed,neginfinity,infinity,closed),
        i(closed,neginfinity,infinity,closed)], [up,down]).

/* absolute value */
mono(abs,[i(closed,neginfinity,-(0),closed)], [down]).
mono(abs,[i(closed,0,infinity,closed)], [up]).

/* multiplication */
mono(*,[i(closed,0,infinity,closed), i(closed,0,infinity,closed)], 
	[up,up]).
mono(*,[i(closed,0,infinity,closed), i(closed,neginfinity,-(0),closed)], 
	[down,up]).
mono(*,[i(closed,neginfinity,-(0),closed), i(closed,0,infinity,closed)], 
	[up,down]).
mono(*,[i(closed,neginfinity,-(0),closed), i(closed,neginfinity,-(0),closed)], 
	[down,down]).



/* division */
mono(/,[i(closed,0,infinity,closed), i(closed,0,infinity,closed)], 
	[up,down]).
mono(/,[i(closed,0,infinity,closed), i(closed,neginfinity,-(0),closed)], 
	[down,down]).
mono(/,[i(closed,neginfinity,-(0),closed), i(closed,0,infinity,closed)], 
	[up,up]).
mono(/,[i(closed,neginfinity,-(0),closed), i(closed,neginfinity,-(0),closed)], 
	[down,up]).


/* exponentiation */
mono(^,[i(open,0,infinity,closed),i(closed,0,infinity,closed)], 
	[up,up]).
mono(^,[i(open,0,infinity,closed),i(closed,neginfinity,-(0),closed)],
	[down,up]).


/* logarithm */
mono(log,[i(closed,0,infinity,closed),i(closed,0,infinity,closed)], 
	[down,up]).

/* sine */
mono(sin,[i(closed,(-90),90,closed)],[up]).
mono(sin,[i(closed,90,270,closed)],[down]).
mono(sin,[i(closed,270,450,closed)],[up]).

/* cosine */
mono(cos,[i(closed,0,180,closed)],[down]).
mono(cos,[i(closed,180,360,closed)],[up]).

/* tangent */
mono(tan,[i(open,(-90),90,open)],[up]).
mono(tan,[i(open,90,270,open)],[up]).
mono(tan,[i(open,270,450,open)],[up]).

/* inverse sine */
mono(arcsin,[i(closed,(-1),1,closed)],[up]).

/* inverse cosine */
mono(arccos,[i(closed,(-1),1,closed)],[down]).

/* inverse tangent */
mono(arctan,[i(open,neginfinity,infinity,open)],[up]).

/* inverse cosecant */
mono(arccsc,[i(closed,neginfinity,(-1),closed)],[down]).
mono(arccsc,[i(closed,1,infinity,closed)],[down]).

/* inverse secant */
mono(arcsec,[i(closed,neginfinity,(-1),closed)],[up]).
mono(arcsec,[i(closed,1,infinity,closed)],[up]).

/* inverse cotangent */
mono(arccot,[i(closed,neginfinity,-(0),open)],[down]).
mono(arccot,[i(open,0,infinity,closed)],[down]).

	


/*************************************************/
/*  Calculate Interval of Angle from Curve Type  */
/*************************************************/


			% We classify a symbol using semantic information
			%  from the (Mecho) database. Calls which are to
			%  this database (notionally, Press does not really
			%  share the same object-level database) are marked
			%  as such.
			% This method is only appropriate if the symbol is an
			%  <angle>, and tries to find the interval of the
			%  angle using general principles about curve types.

classify(Angle, Int ) :-
	measure(Q, Angle ),			% database
	angle(Point, Q, Curve ), !,		% database
	interval(angle, Curve, Int ).

classify(Angle, Int ) :-
	measure(Q, Angle ),			% database
	incline(Curve, Q, Point ), !,		% database
	interval(incline, Curve, Int ).



			% Find interval from curve shape

				% For simple curves
interval(AI, Curve, Int ) :-
	concavity(Curve, Conv ),		% database
	slope(Curve, Slope ), !,		% database
	quad(AI, Slope, Conv, Int ).

				% For complex curves
interval(AI, Curve, Int ) :-
	partition(Curve, Clist ), !,		% database
	collect_intervals(Clist, AI, Rlist),
	gen_combine(Rlist, Int ).



			% Collect up a list of intervals for all the parts
			%  of a partitioned curve.

collect_intervals([],_,[]).

collect_intervals([First|Rest],AI,[FirstInt|RestInt])
     :-	interval(AI,First,FirstInt),
	collect_intervals(Rest,AI,RestInt).



			% Information about properties of simple curves
			%  The interval depends on both the slope and the
			%  concavity.

quad(angle,left,right,i(closed,0,90,closed)) :- !.
quad(incline,left,right,i(closed,90,180,closed)) :- !.

quad(angle,right,right,i(closed,90,180,closed)) :- !.
quad(incline,right,right,i(closed,180,270,closed)) :- !.

quad(angle,left,left,i(closed,180,270,closed)) :- !.
quad(incline,left,left,i(closed,270,360,closed)) :- !.

quad(angle,right,left,i(closed,270,360,closed)) :- !.
quad(incline,right,left,i(closed,0,90,closed)) :- !.

quad(angle,left,stline,i(open,180,270,open)) :- !.
quad(incline,left,stline,i(open,270,360,open)) :- !.

quad(angle,right,stline,i(open,270,360,open)) :- !.
quad(incline,right,stline,i(open,0,90,open)) :- !.

quad(angle,hor,stline,i(closed,270,270,closed)) :- !.
quad(incline,hor,stline,i(closed,0,0,closed)) :- !.

quad(angle,vert,stline,i(closed,180,180,closed)) :- !.
quad(incline,vert,stline,i(closed,270,270,closed)) :- !.



/* JOBS TO DO

	write symbolic version for finding max/mins

	use monotonicity in > >= etc Isolation rules
*/

assumed_positive(_) :- fail.

« December 2024 »
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
 

Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: