% BEGIN LICENSE BLOCK
% Version: CMPL 1.1
%
% The contents of this file are subject to the Cisco-style Mozilla Public
% License Version 1.1 (the "License"); you may not use this file except
% in compliance with the License.  You may obtain a copy of the License
% at www.eclipse-clp.org/license.
% 
% Software distributed under the License is distributed on an "AS IS"
% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
% the License for the specific language governing rights and limitations
% under the License. 
% 
% The Original Code is  The ECLiPSe Constraint Logic Programming System. 
% The Initial Developer of the Original Code is  Cisco Systems, Inc. 
% Portions created by the Initial Developer are
% Copyright (C) 1998 - 2006 Cisco Systems, Inc.  All Rights Reserved.
% 
% Contributor(s): Vassilis Liatsos, IC-Parc
% 
% END LICENSE BLOCK
% ----------------------------------------------------------------------
% System:	ECLiPSe Constraint Logic Programming System
% Version:	$Id: not_equals.pl,v 1.1.1.1 2006/09/23 01:53:51 snovello Exp $
%
% Emacs:    --*-prolog-*--
% not_equals.pl - 
%
% Author:   vassilis liatsos 
% 
% Purpose:  asking whether two variables can  
%           unify or not, taking disequality
%           constraints into account
%
% Usage:   
%     ne/2   asserts a disequality constraint
%     neqt/2  tests whether variables are not equal
%
% This succeeds with the following queries:
%
% ne(X,Y),not_unify(X,Y).
% ne(X,a),not_unify(X,a).
% ne(a,b).
%
% and fails with:
%
% ne(X,X).
% ne(a,a).
% ne(X,Y),X=Y.
% ne(X,a),X=a.
%
% Date: February 98
% ----------------------------------------------------------------------

:- pragma(expand).

:- module(not_equals).


:- export
        ne/2,
        neqt/2.

:- begin_module(not_equals).
:- lib(structures).

:- import
        add_attribute/3,
        setarg/3
    from sepia_kernel.

:- meta_attribute(not_equals,[
                    unify:unify_ne/2,
		    test_unify: test_unify_ne/2,
		    print:print_ne/2]).

:- define_struct(not_equals(ne)).

ne(X,Y):-
	nonvar(X),
	nonvar(Y),!,
	X\=Y.

ne(X,Y):-
	(X==Y -> 
	    fail
        ;
	    (var(X) ->
		get_ne_attr(X,AX),
		AX = not_equals with ne:NeX,
		sort([Y|NeX],NewX),
		setarg(ne of not_equals,AX,NewX)
	    ;
	        true
	    ),
	    
	    (var(Y) ->
		get_ne_attr(Y,AY),
		AY = not_equals with ne:NeY,
		sort([X|NeY],NewY),
		setarg(ne of not_equals,AY,NewY)
	    ;
	        true
	    )
	).

% neqt(+X,+Y)
% if X\=Y can be deduced then succeed
% otherwise fail
 
neqt(X,Y):-
	not_unify(X,Y).

/*****************************
      UTILITY PREDICATES
******************************/

get_ne_attr(X{A},Attr):-
	-?->
	get_ne_attr1(X,Attr,A).
get_ne_attr(X,Attr):-
	free(X),
	new_ne_attr(X,Attr).

get_ne_attr1(X,Attr,A):-
	var(A),new_ne_attr(X,Attr).
get_ne_attr1(_,Attr,A):-
	nonvar(A),Attr = A.

new_ne_attr(X,Attr):-
	Attr = not_equals with ne:[],
	add_attribute(X,Attr).

mem_check(X,[Y|_]):- X==Y,!.
mem_check(X,[_|R]):- mem_check(X,R).

/*****************************
          HANDLERS 
******************************/
		
% unify_ne(+Term, Attribute)
unify_ne(_, Attr):-
        /*** ANY + VAR ***/
        var(Attr).             % Ignore if no attributes for this extension
unify_ne(Term, Attr):-
        compound(Attr),
        unify_term_ne(Term, Attr).

unify_term_ne(Value, Attr):-
        nonvar(Value),         % The metaterm was instantiated
	Attr = not_equals with ne: List,
        /*** NONVAR + META ***/
        not mem_check(Value,List).
unify_term_ne(Y{AttrY},AttrX):-
        -?->
        unify_ne_ne(Y,AttrX,AttrY).

unify_ne_ne(_, AttrX, AttrY):-
        var(AttrY),            % no attribute for this extension
        /*** VAR + META ***/
        AttrX = AttrY.
unify_ne_ne(Y, AttrX, AttrY):-
        nonvar(AttrY),
        /*** META + META ***/
        AttrX = not_equals with ne:NeX,
	AttrY = not_equals with ne:NeY,
	not mem_check(Y,NeX),
	append(NeX,NeY,NeXY),
	sort(NeXY,New),
	setarg(ne of not_equals,AttrY,New).

% test_unify_ne(+Term, Attribute)
test_unify_ne(_, Attr):-
        /*** ANY + VAR ***/
        var(Attr).             % Ignore if no attributes for this extension
test_unify_ne(Term, Attr):-
        compound(Attr),
        test_unify_term_ne(Term, Attr).

test_unify_term_ne(Value, Attr):-
        nonvar(Value),         % The metaterm was instantiated
	Attr = not_equals with ne:List,
        /*** NONVAR + META ***/
        not mem_check(Value,List).
test_unify_term_ne(Y{AttrY},AttrX):-
        -?->
        test_unify_ne_ne(Y,AttrX,AttrY).

test_unify_ne_ne(_, _, AttrY):-
        /*** VAR + META ***/
        var(AttrY).            % no attribute for this extension

test_unify_ne_ne(Y, AttrX, AttrY):-
        nonvar(AttrY),
        /*** META + META ***/
        AttrX = not_equals with ne:NeX,
	not mem_check(Y,NeX).

print_ne(not_equals(NE),Print):- 
        -?->
        Print = ( ne: NE).
