:- module(sd).
:- export alldifferent / 1.
:- export &\= / 3.
:- export &\= / 2.
:- export &= / 3.
:- export &= / 2.
:- export msg / 3.
:- export labeling / 1.
:- export indomain / 1.
:- export deleteff / 3.
:- export get_domain_size / 2.
:- export is_solver_type / 1.
:- export is_exact_solver_var / 1.
:- export is_solver_var / 1.
:- export have_domain / 3.
:- export &:: / 2.
:- export compare_instances_sd / 3.
:- export delayed_goals_number_sd / 2.
:- export suspensions_sd / 3.
:- export copy_term_sd / 2.
:- export test_unify_sd / 2.
:- export unify_sd / 3.
:- export get_domain_as_list / 2.
:- export op(700, xfx, [&::, &=, &\=]).
:- tool(&:: / 2,have_domain / 3).
:- comment(have_domain / 3, hidden).
:- comment(summary, "Simple solver for constraints over unordered symbolic domains").
:- comment(author, "Joachim Schimpf").
:- comment(date, "$Date: 2008/06/05 15:30:43 $").
:- comment(copyright, "Cisco Systems, Inc.").
:- comment(desc, html("\n    <H4>Overview</H4>\n    <P>\n    This is a simple library implementing variables and constraints over\n    atomic values.  Its main purpose is for first experiments with constraint\n    solving.  Moreover, those interested in writing their own constraint\n    solvers can use the source code of this library as a starting point.\n    <H4>Domains</H4>\n    Domains are declared by giving a list of possible values for a variable,\n    e.g.\n    <PRE>\n    \t?- X &amp;:: [red, green, blue].\n\tX = X{[blue, green, red]}\n\tYes (0.00s cpu)\n    </PRE>\n    A variable that has been given a domain can only be instantiated to\n    values from this domain. Any attempt to instantiate the variable to\n    a non-domain value will cause failure:\n    <PRE>\n    \t?- X &amp;:: [red, green, blue], X = red.\n\tX = red\n\tYes (0.00s cpu)\n    \t?- X &amp;:: [red, green, blue], X = yellow.\n\tNo (0.00s cpu)\n    </PRE>\n    <H4>Basic Constraints</H4>\n    There are only two basic constraints, equality and disequality:\n    <DL>\n    <DT>X &amp;= Y</DT><DD>X is the same as Y</DD>\n    <DT>X &amp;\\= Y</DT><DD>X is different from Y</DD>\n    </DL>\n    Both constraints exist in a reified form:\n    <DL>\n    <DT>&amp;=(X,Y,Bool)</DT><DD>Bool is the truth value (0/1) of X &amp;= Y</DD>\n    <DT>&amp;\\=(X,Y,Bool)</DT><DD>Bool is the truth value (0/1) of X &amp;\\= Y</DD>\n    </DL>\n    </PRE>\n    <H4>Global Constraints</H4>\n    One derived, global constraint is implemented:\n    <DL>\n    <DT>alldifferent(List)</DT><DD>All list elements are different</DD>\n    </DL>\n    <H4>Search</H4>\n    Domain variables can be instantiated to their domain values using\n    <DL>\n    <DT>indomain(X)</DT><DD>enumerate values of X</DD>\n    <DT>labeling(Xs)</DT><DD>enumerate values of all elements of list Xs</DD>\n    </DL>\n    ")).
:- comment(&:: / 2, [summary : "All elements of Vars have a value in the domain Domain", args : ["Vars" : "Variable or atomic value, list of them, or submatrix of them", "Domain" : "List of atomic values, or domain name (atom)"], template : "?Vars &:: +Domain", see_also : [indomain / 1, get_domain_as_list / 2, get_domain_size / 2, domain / 1], eg : "\n    ?- X &:: [mo, tu, we, th, fr, sa, su].\n    X = X{[fr, mo, sa, su, th, tu, we]}\n    Yes (0.00s cpu)\n\n    ?- [X,Y,we] &:: [mo, tu, we, th, fr, sa, su].\n    X = X{[fr, mo, sa, su, th, tu, we]}\n    Y = Y{[fr, mo, sa, su, th, tu, we]}\n    Yes (0.00s cpu)\n\n    ?- dim(M, [3]), M[1..3] &:: [a,e,i,o,u].\n    M = [](_354{[a,e,i,o,u]}, _364{[a,e,i,o,u]}, _374{[a,e,i,o,u]})\n    Yes (0.00s cpu)\n\n    ?- [X,Y] &:: [we,fr,su].\n    X = X{[we, fr, su]}\n    Y = Y{[we, fr, su]}\n\n    ?- X &:: [].\n    No (0.00s cpu)\n\n    % Using a named, pre-declared domain\n    ?- local domain(colour(r, g, b)).\n    Yes (0.01s cpu)\n\n    ?- X &:: colour.\n    X = X{[b, g, r]}\n    Yes (0.00s cpu)\n    ", desc : html("<P>\n\tConstrains a variable (or a list of variables, or a submatrix of\n\tvariables), to have the domain Domain.  The domain is specified\n\tsimply as an unordered list of atomic values.  A domain variable\n\tcan only be instantiated to values within its domain.\n\t</P><P>\n\tFor compatibility with library(ic_symbolic), domains can also be\n\tpre-declared using domain/1, and domain variables then declared\n\tby just giving the domain name rather than the list of values.\n\tNote however that even in this case the domain is considered\n\tunordered, i.e. the order of domain elements in the declaration\n\tis not important for the purposes of this library.\n\t</P><P>\n\tNote that, on the left hand side of &amp;::/2, the atom [] is not\n\tinterpreted as the empty list (of variables or values), but as\n\ta single atomic value, i.e. a potential domain element.\n\tThis is in keeping with the behaviour of the ic_symbolic\n\tlibrary, but in contrast with the behaviour of numeric solvers\n\tsuch as ic.\n\t</P>\n")]).
:- comment(&= / 2, [summary : "X is the same atomic value as Y", args : ["X" : "Variable or atomic value", "Y" : "Variable or atomic value"], template : "?X &= ?Y", see_also : [&= / 3, &\= / 2], eg : "\n    ?- X &:: [red,green,blue], X &= red.\n    X = red\n    Yes (0.00s cpu)\n\n    ?- X &:: [red,green,blue], X &= yellow.\n    No (0.00s cpu)\n\n    ?- [X, Y] &:: [red,green,blue], X &= Y.\n    X = X{[blue, green, red]}\n    Y = Y{[blue, green, red]}\n    There is 1 delayed goal.\n    Yes (0.00s cpu)\n\n    ?- X &:: [red,green,blue], X &= Y.\n    X = X{[blue, green, red]}\n    Y = Y{[blue, green, red]}\n    There is 1 delayed goal.\n    Yes (0.00s cpu)\n\n    ?- X &:: [red,green,blue], Y &:: [blue,yellow,green], X &= Y.\n    X = X{[blue, green]}\n    Y = Y{[blue, green]}\n    There is 1 delayed goal.\n    Yes (0.00s cpu)\n\n    ?- X &:: [red, green, blue], Y &:: [blue, yellow, brown], X &= Y.\n    X = blue\n    Y = blue\n    Yes (0.00s cpu)\n\n    ?- red &= red.\n    Yes (0.00s cpu)\n\n    ?- X &= red.\n    X = red\n    Yes (0.00s cpu)\n    ", desc : html("\n\tConstrains X and Y to be the same.  This is very similar to unifying\n\tX and Y, except in the case where X and Y are both variables. While\n\tunification turns the two variables into one, this constraint preserves\n\tthe separate variables but constrains them to be equal.\n")]).
:- comment(&= / 3, [summary : "Reified version of X &= Y", see_also : [&= / 2], args : ["X" : "Variable or atomic value", "Y" : "Variable or atomic value", "Bool" : "0, 1, or boolean variable"]]).
:- comment(&\= / 2, [summary : "X is different from Y", args : ["X" : "Variable or atomic value", "Y" : "Variable or atomic value"], template : "?X &\\= ?Y", see_also : [&\= / 3, &= / 2], eg : "\n    ?- X &:: [red,green,blue], X &\\= red.\n    X = X{[blue, green]}\n    Yes (0.00s cpu)\n\n    ?- X &:: [red, green, blue], X &\\= yellow.\n    X = X{[blue, green, red]}\n    Yes (0.00s cpu)\n\n    ?- X &:: [red,green], X &\\= red.\n    X = green\n    Yes (0.00s cpu)\n\n    ?- X &:: [red,green], X &\\= red, X &\\= green.\n    No (0.00s cpu)\n\n    ?- X &\\= red.\n    X = X\n    There is 1 delayed goal.\n    Yes (0.00s cpu)\n\n    ?- X &\\= red, X &:: [red,green].\n    X = green\n    Yes (0.00s cpu)\n\n    ?- [X, Y] &:: [red, green, blue], X &\\= Y.\n    X = X{[blue, green, red]}\n    Y = Y{[blue, green, red]}\n    There is 1 delayed goal.\n    Yes (0.00s cpu)\n\n    ?- X &:: [red, green], Y &:: [blue, brown], X &\\= Y.\n    X = X{[green, red]}\n    Y = Y{[blue, brown]}\n    There is 1 delayed goal.\n    Yes (0.00s cpu)\n\n    ?- red &\\= green.\n    Yes (0.00s cpu)\n\n    ?- red &\\= red.\n    No (0.00s cpu)\n    ", desc : html("\n\tConstrains X and Y to be different. Operationally, the predicate\n\tdelays until one side is instantiated and the other has a domain.\n\tThe instantiated value is then removed from the domain.\n")]).
:- comment(&\= / 3, [summary : "Reified version of X &\\= Y", see_also : [&\= / 2], args : ["X" : "Variable or atomic value", "Y" : "Variable or atomic value", "Bool" : "0, 1, or boolean variable"]]).
:- comment(alldifferent / 1, [summary : "All elements of List are different", args : ["List" : "List of domain variables or atomic values"], see_also : [&\= / 2], eg : "\n    ?- [X, Y, Z] &:: [a,b,c,d], alldifferent([X,Y,Z]), X = a.\n    X = a\n    Y = Y{[b, c, d]}\n    Z = Z{[b, c, d]}\n    There are 2 delayed goals.\n    Yes (0.00s cpu)\n\n    ?- X &:: [a,b,c], alldifferent([a,b,X]).\n    X = c\n    Yes (0.00s cpu)\n    ", desc : html("<P>\n\tConstrains all list elements to be different atomic values.\n\tAt call time, the list elements must already have domains or\n\tbe instantiated.\n\t</P><P>\n\tOperationally, the predicate delays until list elements become\n\tinstatiated, and removes the corresponding values from the domains\n\tof the other list elements.\n\t</P>\n")]).
:- comment(deleteff / 3, [summary : "Pick minimum domain size element from a list (first fail principle)", amode : deleteff(-, +, -), args : ["Min" : "Smallest list element in terms of domain size", "List" : "List of domain variables or constants", "Rest" : "Remaining list without smallest element"], see_also : [get_domain_size / 2], fail_if : "List is empty", desc : html("\n\tExtracts from a list of domain variables (or constants) the first\n\telement with the smallest domain. This element is returned as Min,\n\tand the remaining list without this element is returned as Rest.\n\tThis predicate is useful for implementing the first-fail-principle\n\tin a search procedure, i.e. labeling the variable with the smallest\n\tdomain first. E.g.\n\t<PRE>\n\tlabelingff(List) :-\n\t\t( deleteff(X, List, Rest) ->\n\t\t    indomain(X),\n\t\t    labelingff(Rest)\n\t\t;\n\t\t    true\n\t\t).\n\t</PRE>\n")]).
:- comment(get_domain_as_list / 2, [summary : "Retrieves the domain of a variable (or value) as a list of values", amode : get_domain_as_list(?, -), args : ["Term" : "Domain variable or atomic value", "List" : "List of atomic values"], see_also : [&:: / 2, indomain / 1, get_domain_size / 2], desc : html("\n\tRetrieves the domain of a variable (or value) as a list of values.\n\tThe list is an ordered set in the sense of library(ordset), i.e.\n\ta duplicate-free list in ascending term order.\n")]).
:- comment(get_domain_size / 2, [summary : "Gives the size of the domain of a variable (or value)", amode : get_domain_size(?, -), args : ["Term" : "Domain variable or atomic value", "Size" : "Variable or integer"], see_also : [&:: / 2, get_domain_as_list / 2, deleteff / 3], desc : html("\n\tRetrieves the domain size of a domain variable (or value).\n\tIt Term is instantiated, Size is 1.\n")]).
:- comment(indomain / 1, [summary : "Nondeterministically instantiate to domain values", args : ["X" : "Domain variable or value"], see_also : [&:: / 2], eg : "\n    ?- X &:: [mo, tu, we, th, fr, sa, su], indomain(X).\n    X = fr\n    Yes (0.00s cpu, solution 1, maybe more)\n    X = mo\n    Yes (0.05s cpu, solution 2, maybe more)\n    X = sa\n    Yes (0.06s cpu, solution 3, maybe more)\n    X = su\n    Yes (0.06s cpu, solution 4, maybe more)\n    X = th\n    Yes (0.06s cpu, solution 5, maybe more)\n    X = tu\n    Yes (0.07s cpu, solution 6, maybe more)\n    X = we\n    Yes (0.08s cpu, solution 7)\n\n    ?- indomain(we).\n    Yes (0.00s cpu)\n    ", desc : html("<P>\n\tNondeterministically instantiates a domain variable to its domain\n\tvalues. The order of enumeration is in increasing term order (i.e.\n\talphabetic for atoms).\n    </P>")]).
:- comment(is_exact_solver_var / 1, [summary : "The argument is a domain variable", amode : is_exact_solver_var(?), args : ["Term" : "A term"], see_also : [is_solver_var / 1], desc : html("\n\tTests if the argument is a domain variable from this library.\n\tAn alias for is_solver_var/1, used by lib(propia).\n")]).
:- comment(is_solver_type / 1, [summary : "The argument is a domain variable or atomic constant", amode : is_solver_type(?), args : ["Term" : "A term"], see_also : [&:: / 2, is_solver_var / 1], desc : html("\n\tTests if the argument is a domain variable from this library\n\tor a valid domain value (atomic term).\n")]).
:- comment(is_solver_var / 1, [summary : "The argument is a domain variable", amode : is_solver_var(?), args : ["Term" : "A term"], see_also : [&:: / 2, is_solver_type / 1], desc : html("\n\tTests if the argument is a domain variable from this library.\n")]).
:- comment(labeling / 1, [summary : "Instantiate all domain variables in a list to domain values", amode : labeling(+), args : ["Term" : "A list of domain variables or atomic terms"], see_also : [&:: / 2, indomain / 1], desc : html("\n\tInstantiate all domain variables in a list to their domain values.\n\tAlternative instantiations are tried on backtracking. This predicate\n\tsimply calls indomain/1 on all variable in the given list.\n")]).
:- comment(msg / 3, [summary : "MSG is the most specific generalisation of X and Y representable with domain variables from this library", args : ["X" : "Any term or variable", "Y" : "Any term or variable", "MSG" : "A domain variable or constant (output)"], amode : msg(?, ?, -), fail_if : "None", see_also : [_23629 : msg / 3, library(propia), &:: / 2], eg : "\n    ?- msg(we, fr, Z).\n    Z = Z{[we, fr]}\n    Yes (0.00s cpu)\n\n    ?- X &:: [sa, su], msg(X, we, Z).\n    X = X{[sa, su]}\n    Z = Z{[we, sa, su]}\n    Yes (0.00s cpu)\n\n    ?- X &:: [sa, su], Y &:: [mo, tu, we], msg(X, Y, Z).\n    X = X{[sa, su]}\n    Y = Y{[mo, tu, we]}\n    Z = Z{[mo, tu, we, sa, su]}\n    Yes (0.00s cpu)\n\n    ?- X &:: [sa, su], msg(X, _, Z).\n    X = X{[sa, su]}\n    Z = Z\n    Yes (0.01s cpu)\n\n    ?- msg(we, we, X).\n    X = we\n    Yes (0.00s cpu)\n    ", desc : html("<P>\n\tThis predicate computes the most specific generalisation of X and Y\n\twhich can be represented using this library's domain variables.\n\t</P><P>\n\tIf X and Y are domain variables (or atomic constants), then MSG\n\twill be unified with a new domain variable whose domain\n\tconsists of the union of the domain elements of X and Y.\n\tIf the domain union contains only a single value, the result\n\tis this single value.\n\t</P><P>\n\tIf X or Y are free (unconstrained) variables, then the result will\n\talso be a free (unconstrained) variable.\n    </P>")]).
