/**
 * Warranty & Liability
 * To the extent permitted by applicable law and unless explicitly
 * otherwise agreed upon, XLOG Technologies AG makes no warranties
 * regarding the provided information. XLOG Technologies AG assumes
 * no liability that any problems might be solved with the information
 * provided by XLOG Technologies AG.
 *
 * Rights & License
 * All industrial property rights regarding the information - copyright
 * and patent rights in particular - are the sole property of XLOG
 * Technologies AG. If the company was not the originator of some
 * excerpts, XLOG Technologies AG has at least obtained the right to
 * reproduce, change and translate the information.
 *
 * Reproduction is restricted to the whole unaltered document. Reproduction
 * of the information is only allowed for non-commercial uses. Selling,
 * giving away or letting of the execution of the library is prohibited.
 * The library can be distributed as part of your applications and libraries
 * for execution provided this comment remains unchanged.
 *
 * Restrictions
 * Only to be distributed with programs that add significant and primary
 * functionality to the library. Not to be distributed with additional
 * software intended to replace any components of the library.
 *
 * Trademarks
 * Jekejeke is a registered trademark of XLOG Technologies AG.
 */

:- op(700, xfx, in).
:- op(700, xfx, ins).
:- op(700, xfx, #\=).
:- op(700, xfx, #=).
:- op(700, xfx, #<).
:- op(700, xfx, #=<).
:- op(700, xfx, #>).
:- op(700, xfx, #>=).
:- op(700, xfx, ::).

:- op(600, xfx, ..).

:- ensure_loaded(library(sets)).
:- ensure_loaded(library(lists)).
:- ensure_loaded(library(edge/attrvars)).

/**************************************************************/
/* in/2 and ins/2                                             */
/**************************************************************/

/**
 * T in D:
 * The predicate delays the goal in D on the term T.
 */
% in(+Term, +Range)
in(T, L..H) :- deref(T, V), freeze(T, sys_mach_bound(L, H, V)).

/**
 * sys_mach_bound(L, H, X):
 * The predicate succeeds whenever between(L', H', X) succeeds,
 * where L' and H' are the Prolog evaluation of L and H.
 */
% sys_mach_bound(+Eval, +Eval, -Integer)
sys_mach_bound(P, Q, X) :-
   L is P, H is Q,
   between(L, H, X).

/**
 * L ins D:
 * The predicate delays the goal in D on the elements L.
 */
% ins(+List, +Range)
ins([], _).
ins([X|L], D) :-
   in(X, D),
   ins(L, D).

/**************************************************************/
/* (#=)/2, (#\=)/2, (#<)/2, (#=<)/2, (#>)/2 and (#>=)/2       */
/**************************************************************/

/**
 * S #= T:
 * The predicate delays the goal S =:= T on the terms S and T.
 */
% +Term #= +Term
A #= B :-
   sys_attr_expr(A, C, [], H),
   sys_attr_expr(B, D, H, M),
   sys_attr_delay(M, C =:= D).

/**
 * S #\= T:
 * The predicate delays the goal S =\= T on the terms S and T.
 */
% +Term #\= +Term
A #\= B :-
   sys_attr_expr(A, C, [], H),
   sys_attr_expr(B, D, H, M),
   sys_attr_delay(M, C =\= D).

/**
 * S #< T:
 * The predicate delays the goal S < T on the terms S and T.
 */
% +Term #< +Term
A #< B :-
   sys_attr_expr(A, C, [], H),
   sys_attr_expr(B, D, H, M),
   sys_attr_delay(M, C < D).

/**
 * S #=< T:
 * The predicate delays the goal S =< T on the terms S and T.
 */
% +Term #=< +Term
A #=< B :-
   sys_attr_expr(A, C, [], H),
   sys_attr_expr(B, D, H, M),
   sys_attr_delay(M, C =< D).

/**
 * S #> T:
 * The predicate delays the goal S > T on the terms S and T.
 */
% +Term #> +Term
A #> B :-
   sys_attr_expr(A, C, [], H),
   sys_attr_expr(B, D, H, M),
   sys_attr_delay(M, C > D).

/**
 * S #>= T:
 * The predicate delays the goal S >= T on the terms S and T.
 */
% +Term #>= +Term
A #>= B :-
   sys_attr_expr(A, C, [], H),
   sys_attr_expr(B, D, H, M),
   sys_attr_delay(M, C >= D).

% sys_attr_delay(+Map, +Goal)
sys_attr_delay([], G) :- !, sys_site_cond(G, F), F.
sys_attr_delay(M, G) :- sys_attr_post(M, G).

% sys_attr_post(+Map, +Goal)
sys_attr_post([], _).
sys_attr_post(['$ATTR'(_,L)|M], G) :- sys_attr_freeze(L, G), sys_attr_post(M, G).

% sys_attr_expr(+Term, -Term, +Map, -Map)
sys_attr_expr(T, (V :: D), M, N) :- T = '$ATTR'(V,R), var(V), !,
   sys_attr_range(R, D),
   sys_attr_extend(T, M, N).
sys_attr_expr(-A, -B, M, N) :- !,
   sys_attr_expr(A, B, M, N).
sys_attr_expr(A+B, C+D, M, N) :- !,
   sys_attr_expr(A, C, M, H),
   sys_attr_expr(B, D, H, N).
sys_attr_expr(A-B, C-D, M, N) :- !,
   sys_attr_expr(A, C, M, H),
   sys_attr_expr(B, D, H, N).
sys_attr_expr(A*B, C*D, M, N) :- !,
   sys_attr_expr(A, C, M, H),
   sys_attr_expr(B, D, H, N).
sys_attr_expr(T, V, M, M) :-
   deref(T, V).

% sys_attr_extend(+Term, +Map, -Map)
sys_attr_extend('$ATTR'(V,_), M, M) :- member('$ATTR'(W,_), M), V == W, !.
sys_attr_extend(T, M, [T|M]).

% sys_attr_range(+List, -Range)
sys_attr_range(R, _) :- var(R),
   throw(error(resource_error(range_missing), _)).
sys_attr_range([sys_mach_bound(L, H, _)|_], L..H) :- !.
sys_attr_range(_, _) :-
   throw(error(resource_error(range_missing), _)).

/**************************************************************/
/* all_different/1, label/1 and labeling/2                    */
/**************************************************************/

/**
 * all_different(L):
 * The predicate delays the goal that all elements of L are different.
 */
% all_different(+List)
all_different([]).
all_different([X|L]) :-
   maplist(#\=(X), L),
   all_different(L).

/**
 * label(L):
 * The predicate resolves the elements of L, left to right.
 */
% label(+List)
label(L) :-
   catch((sys_collect_plan(L, P), throw(sys_ball(P))),
      sys_ball(V),
      sys_collect_doit(L, [], V, R, [])),
%   writeq(R), nl,
   '$SEQ'(nothing, R).

/**
 * labeling(O, L):
 * The predicate resolves the elements of L, statically reordered
 * accordingly to the option list O. For the available options
 * see the documentation.
 */
% labeling(+List, +List)
labeling(O, L) :-
   sys_attr_opts(O, 2, F),
   catch((sys_reorder_plan(L, Q, F), throw(sys_ball(Q))),
      sys_ball(U),
      sys_reorder_doit(L, U, R)),
   label(R).

% sys_attr_opts(+List, +Integer, -Integer)
sys_attr_opts(V, _, _) :- var(V),
   throw(error(instantiation_error,_)).
sys_attr_opts([X|L], I, O) :- !,
   sys_attr_opt(X, I, H),
   sys_attr_opts(L, H, O).
sys_attr_opts([], I, I) :- !.
sys_attr_opts(L, _, _) :-
   throw(error(type_error(list,L),_)).

% sys_attr_opt(+Term, +Integer, -Integer)
sys_attr_opt(V, _, _) :- var(V),
   throw(error(instantiation_error,_)).
sys_attr_opt(zero, _, 0) :- !.
sys_attr_opt(constr, _, 1) :- !.
sys_attr_opt(plausi, _, 2) :- !.
sys_attr_opt(O, _, _) :-
   throw(error(domain_error(score_opt,O),_)).

/***********************************************************/
/* Condition Filtering                                     */
/***********************************************************/

% sys_filter_plan(+List, +Integer, -List)
sys_filter_plan(V, _, []) :- var(V), !.
sys_filter_plan([G|L], K, [I|Q]) :- term_variables(G, R), R \== [], !,
   (R = [_] -> I = K; I is (-K)-1),
   J is K+1,
   sys_filter_plan(L, J, Q).
sys_filter_plan([_|L], K, Q) :-
   J is K+1,
   sys_filter_plan(L, J, Q).

% sys_filter_doit(+List, +List, +List, -List, +List)
sys_filter_doit([], _, _) --> [].
sys_filter_doit([I|Q], H, L) --> {I >= 0}, !,
   {nth0(I, L, G), sys_site_cond(G, A)},
   ({A \== true} -> [A]; []),
   sys_filter_doit(Q, H, L).
sys_filter_doit([I|Q], H, L) -->
   {J is (-I)-1, nth0(J, L, G), sys_attr_test(G, H, F, [])},
   sys_site_emit(F),
   sys_filter_doit(Q, H, L).

% sys_collect_plan(+List, -List)
sys_collect_plan([], []).
sys_collect_plan([X|R], [Q|P]) :- X = '$ATTR'(V,L), var(V), !,
   sys_filter_plan(L, 0, Q),
   X = '$ATTR'(*,_),
   sys_collect_plan(R, P).
sys_collect_plan([_|L], P) :-
   sys_collect_plan(L, P).

% sys_collect_doit(+List, +List, +List, -List, +List)
sys_collect_doit([], _, []) --> [].
sys_collect_doit([X|R], H, [Q|S]) --> {X = '$ATTR'(V,L), var(V)}, !,
   sys_filter_bound(Q, P, V, H, L, M),
   sys_filter_doit(P, [V|H], M),
   sys_collect_doit(R, [V|H], S).
sys_collect_doit([_|L], H, P) -->
   sys_collect_doit(L, H, P).

% sys_filter_bound(List, -List, +Var, +List, +List, -List, -List, +List)
sys_filter_bound([], [], _, _, L, L) --> [].
sys_filter_bound([I|Q], P, V, H, [A|L], S) --> {I >= 0,
   nth0(I, [A|L], G), sys_attr_crisp(G, V, F, E)}, !,
   {sys_site_cond(E, C)},
   ({C \== true} -> [C]; []),
   {sys_attr_fuse(F, A, B)},
   sys_filter_bound(Q, P, V, H, [B|L], S).
sys_filter_bound([I|Q], P, V, H, [A|L], S) --> {I < 0,
   J is (-I)-1, nth0(J, [A|L], G), sys_attr_coarse(G, V, H, F, E, [])}, !,
   sys_site_emit(E),
   {sys_attr_fuse(F, A, B)},
   sys_filter_bound(Q, P, V, H, [B|L], S).
sys_filter_bound([I|Q], [I|P], V, H, L, S) -->
   sys_filter_bound(Q, P, V, H, L, S).

/**
 * sys_site_emit(L, I, O):
 * The predicate succeeds with the compilation of L.
 */
% sys_site_emit(+List, -List, +List)
sys_site_emit([]) --> [].
sys_site_emit([G|L]) -->
   {sys_site_cond(G, A)},
   ({A \== true} -> [A]; []),
   sys_site_emit(L).

% sys_attr_fuse(+Goal, +Goal, -Goal)
sys_attr_fuse(true, G, G) :- !.
sys_attr_fuse(sys_mach_bound(C, D, X),
              sys_mach_bound(A, B, X),
              sys_mach_bound(max(A,C), min(B,D), X)).

/***********************************************************/
/* Variable Ordering                                       */
/***********************************************************/

% sys_reorder_plan(+List, -List, +Integer)
sys_reorder_plan([], [], _).
sys_reorder_plan([X|L], [I|R], F) :- X = '$ATTR'(V,U), var(V), !,
   sys_attr_score(U, S, F),
   sys_attr_best(L, S, 1, 0, I, F),
   nth0(I, [X|L], Y, H),
   Y = '$ATTR'(*,_),
   sys_reorder_plan(H, R, F).
sys_reorder_plan([_|L], R, F) :-
   sys_reorder_plan(L, R, F).

% sys_reorder_doit(+List, +List, -List)
sys_reorder_doit([], [], []).
sys_reorder_doit([X|L], [I|Q], [Y|R]) :- X = '$ATTR'(V,_), var(V), !,
   nth0(I, [X|L], Y, H),
   sys_reorder_doit(H, Q, R).
sys_reorder_doit([_|L], Q, R) :-
   sys_reorder_doit(L, Q, R).

/**
 * sys_attr_score(L, S, F):
 * The predicate succeeds in S with a pair of the number of free variables
 * in the constraints, and the negation of the number of constraints
 * for the attributed variable X.
 */
% sys_attr_score(+List, -Integer, +Integer)
sys_attr_score(L, S-U, F) :-
   term_variables(L, H),
   length(H, S),
   sys_attr_skim(L, F, 0, U).

% sys_attr_best(+List, +Integer, +Integer, +Integer, -Integer, +Integer)
sys_attr_best([], _, _, I, I, _).
sys_attr_best(['$ATTR'(V,U)|L], S, K, _, O, F) :- var(V),
   sys_attr_score(U, T, F), T @=< S, !,
   H is K+1,
   sys_attr_best(L, T, H, K, O, F).
sys_attr_best([_|L], S, K, I, O, F) :-
   H is K+1,
   sys_attr_best(L, S, H, I, O, F).

% sys_attr_skim(+List, +Integer, +Integer, -Integer, +Integer)
sys_attr_skim(V, _, U, U) :- var(V), !.
sys_attr_skim([G|L], 2, U, V) :- term_variables(G, R), R \= [], !,
   length(R, J),
   H is U-1/J,
   sys_attr_skim(L, 2, H, V).
sys_attr_skim([G|L], 1, U, V) :- term_variables(G, [_]), !,
   H is U-1,
   sys_attr_skim(L, 1, H, V).
sys_attr_skim([_|L], F, U, V) :-
   sys_attr_skim(L, F, U, V).

/***********************************************************/
/* Partial Consistency                                     */
/***********************************************************/

% sys_attr_test(+Goal, +List, -List, +List)
sys_attr_test(A =:= B, H) --> !,
   sys_attr_inter(A, H, C),
   sys_attr_inter(B, H, D),
   sys_inter_eq(C, D).
sys_attr_test(A =< B, H) --> !,
   sys_attr_inter(A, H, C),
   sys_attr_inter(B, H, D),
   sys_inter_lq(C, D).
sys_attr_test(A < B, H) --> !,
   sys_attr_inter(A, H, C),
   sys_attr_inter(B, H, D),
   sys_inter_ls(C, D).
sys_attr_test(A >= B, H) --> !,
   sys_attr_inter(A, H, C),
   sys_attr_inter(B, H, D),
   sys_inter_gq(C, D).
sys_attr_test(A > B, H) --> !,
   sys_attr_inter(A, H, C),
   sys_attr_inter(B, H, D),
   sys_inter_gr(C, D).
sys_attr_test(_, _) --> [].

% sys_inter_eq(+Pair, +Pair, -List, +List)
sys_inter_eq(L1..H1, L2..H2) --> !, [L1 =< H2, L2 =< H1].
sys_inter_eq(L1..H1, P2) --> !, sys_inter_is(V, P2), [L1 =< V, V =< H1].
sys_inter_eq(P1, L2..H2) --> !, sys_inter_is(U, P1), [U =< H2, L2 =< U].
sys_inter_eq(P1, P2) --> [P1 =:= P2].

% sys_inter_lq(+Pair, +Pair, -List, +List)
sys_inter_lq(L1.._, _..H2) --> !, [L1 =< H2].
sys_inter_lq(L1.._, P2) --> !, [L1 =< P2].
sys_inter_lq(P1, _..H2) --> !, [P1 =< H2].
sys_inter_lq(P1, P2) --> [P1 =< P2].

% sys_inter_ls(+Pair, +Pair, -List, +List)
sys_inter_ls(L1.._, _..H2) --> !, [L1 < H2].
sys_inter_ls(L1.._, P2) --> !, [L1 < P2].
sys_inter_ls(P1, _..H2) --> !, [P1 < H2].
sys_inter_ls(P1, P2) --> [P1 < P2].

% sys_inter_gq(+Pair, +Pair, -List, +List)
sys_inter_gq(_..H1, L2.._) --> !, [H1 >= L2].
sys_inter_gq(_..H1, P2) --> !, [H1 >= P2].
sys_inter_gq(P1, L2.._) --> !, [P1 >= L2].
sys_inter_gq(P1, P2) --> [P1 >= P2].

% sys_inter_gr(+Pair, +Pair, -List, +List)
sys_inter_gr(_..H1, L2.._) --> !, [H1 > L2].
sys_inter_gr(_..H1, P2) --> !, [H1 > P2].
sys_inter_gr(P1, L2.._) --> !, [P1 > L2].
sys_inter_gr(P1, P2) --> [P1 > P2].

/***********************************************************/
/* Interval Arithmetic                                     */
/***********************************************************/

% sys_attr_inter(+Expr, +List, -Pair, -List, +List)
sys_attr_inter(T, H, T) --> {T = (V :: _), occurs_check(V, H)}, !.
sys_attr_inter((_ :: D), _, D) --> !.
sys_attr_inter(-A, H, R) --> !,
   sys_attr_inter(A, H, B),
   sys_inter_neg(B, R).
sys_attr_inter(A+B, H, R) --> !,
   sys_attr_inter(A, H, C),
   sys_attr_inter(B, H, D),
   sys_inter_add(C, D, R).
sys_attr_inter(A-B, H, R) --> !,
   sys_attr_inter(A, H, C),
   sys_attr_inter(B, H, D),
   sys_inter_sub(C, D, R).
sys_attr_inter(A*B, H, R) --> !,
   sys_attr_inter(A, H, C),
   sys_attr_inter(B, H, D),
   sys_inter_mul(C, D, R).
sys_attr_inter(T, _, T) --> [].

% sys_inter_neg(+Pair, -Pair, -List, +List)
sys_inter_neg(L..H, -H.. -L) --> !.
sys_inter_neg(P, -P) --> [].

% sys_inter_add(+Pair, +Pair, -Pair, -List, +List)
sys_inter_add(L1..H1, L2..H2, L1+L2..H1+H2) --> !.
sys_inter_add(L1..H1, P2, L1+V..H1+V) --> !, sys_inter_is(V, P2).
sys_inter_add(P1, L2..H2, U+L2..U+H2) --> !, sys_inter_is(U, P1).
sys_inter_add(P1, P2, P1+P2) --> [].

% sys_inter_sub(+Pair, +Pair, -Pair, -List, +List)
sys_inter_sub(L1..H1, L2..H2, L1-H2..H1-L2) --> !.
sys_inter_sub(L1..H1, P2, L1-V..H1-V) --> !, sys_inter_is(V, P2).
sys_inter_sub(P1, L2..H2, U-H2..U-L2) --> !, sys_inter_is(U, P1).
sys_inter_sub(P1, P2, P1-P2) --> [].

% sys_inter_mul(+Pair, +Pair, -Pair, -List, +List)
sys_inter_mul(L1..H1, L2..H2, min(min(W11,W12),min(W21,W22))..
                        max(max(W11,W12),max(W21,W22))) --> !,
   sys_inter_is(U1, L1), sys_inter_is(U2, H1),
   sys_inter_is(V1, L2), sys_inter_is(V2, H2),
   sys_inter_is(W11, U1*V1), sys_inter_is(W12, U1*V2),
   sys_inter_is(W21, U2*V1), sys_inter_is(W22, U2*V2).
sys_inter_mul(L1..H1, P2, min(W1,W2)..max(W1,W2)) --> !,
   sys_inter_is(U1, L1), sys_inter_is(U2, H1),
   sys_inter_is(V, P2),
   sys_inter_is(W1, U1*V), sys_inter_is(W2, U2*V).
sys_inter_mul(P1, L2..H2, min(W1,W2)..max(W1,W2)) --> !,
   sys_inter_is(U, P1),
   sys_inter_is(V1, L2), sys_inter_is(V2, H2),
   sys_inter_is(W1, U*V1), sys_inter_is(W2, U*V2).
sys_inter_mul(P1, P2, P1*P2) --> [].

% sys_inter_is(-Expr, +Expr)
sys_inter_is(T, T) --> {T = (_::_)}, !.
sys_inter_is(X:: -0rInf..0rInf, E) --> [X is E].

/***********************************************************/
/* Bound Indexicals                                        */
/***********************************************************/

% sys_attr_crisp(+Goal, +Var, -Goal, -Goal))
sys_attr_crisp(A =:= B, W, G2, G1) :-
   sys_attr_linear(A, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_sub(P, R, U),
   sys_simp_sub(S, Q, V),
   (U = 1 ->
      G1 = (W is V),
      G2 = true;
    U = -1 ->
      sys_simp_neg(V, K),
      G1 = (W is K),
      G2 = true;
    fail).
sys_attr_crisp(A =< B, W, G2, G1) :-
   sys_attr_linear(A, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_sub(P, R, U),
   sys_simp_sub(S, Q, V),
   (U = 1 ->
      G1 = true,
      G2 = sys_mach_bound(-0rInf, V, W);
    U = -1 ->
      sys_simp_neg(V, K),
      G1 = true,
      G2 = sys_mach_bound(K, 0rInf, W);
    fail).
sys_attr_crisp(A < B, W, G2, G1) :-
   sys_attr_linear(A+1, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_sub(P, R, U),
   sys_simp_sub(S, Q, V),
   (U = 1 ->
      G1 = true,
      G2 = sys_mach_bound(-0rInf, V, W);
    U = -1 ->
      sys_simp_neg(V, K),
      G1 = true,
      G2 = sys_mach_bound(K, 0rInf, W);
    fail).
sys_attr_crisp(B >= A, W, G2, G1) :-
   sys_attr_linear(A, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_sub(P, R, U),
   sys_simp_sub(S, Q, V),
   (U = 1 ->
      G1 = true,
      G2 = sys_mach_bound(-0rInf, V, W);
    U = -1 ->
      sys_simp_neg(V, K),
      G1 = true,
      G2 = sys_mach_bound(K, 0rInf, W);
    fail).
sys_attr_crisp(B > A, W, G2, G1) :-
   sys_attr_linear(A+1, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_sub(P, R, U),
   sys_simp_sub(S, Q, V),
   (U = 1 ->
      G1 = true,
      G2 = sys_mach_bound(-0rInf, V, W);
    U = -1 ->
      sys_simp_neg(V, K),
      G1 = true,
      G2 = sys_mach_bound(K, 0rInf, W);
    fail).

% sys_attr_coarse(+Goal, +Var, +List, -Goal, -List, +List)
sys_attr_coarse(A =:= B, W, H, G) -->
   {sys_attr_linear(A, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_sub(P, R, U),
   sys_simp_sub(S, Q, V)},
   ({U = 1} ->
      sys_attr_inter(V, H, E),
      sys_inter_bound(E, W, G);
    {U = -1} ->
      {sys_simp_neg(V, K)},
      sys_attr_inter(K, H, E),
      sys_inter_bound(E, W, G);
    {fail}).
sys_attr_coarse(A =< B, W, H, G) -->
   {sys_attr_linear(A, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_sub(P, R, U),
   sys_simp_sub(S, Q, V)},
   ({U = 1} ->
      sys_attr_inter(V, H, E),
      {sys_inter_lower(E, W, G)};
    {U = -1} ->
      {sys_simp_neg(V, K)},
      sys_attr_inter(K, H, E),
      {sys_inter_upper(E, W, G)};
    {fail}).
sys_attr_coarse(A < B, W, H, G) -->
   {sys_attr_linear(A+1, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_sub(P, R, U),
   sys_simp_sub(S, Q, V)},
   ({U = 1} ->
      sys_attr_inter(V, H, E),
      {sys_inter_lower(E, W, G)};
    {U = -1} ->
      {sys_simp_neg(V, K)},
      sys_attr_inter(K, H, E),
      {sys_inter_upper(E, W, G)};
    {fail}).
sys_attr_coarse(B >= A, W, H, G) -->
   {sys_attr_linear(A, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_sub(P, R, U),
   sys_simp_sub(S, Q, V)},
   ({U = 1} ->
      sys_attr_inter(V, H, E),
      {sys_inter_lower(E, W, G)};
    {U = -1} ->
      {sys_simp_neg(V, K)},
      sys_attr_inter(K, H, E),
      {sys_inter_upper(E, W, G)};
    {fail}).
sys_attr_coarse(B > A, W, H, G) -->
   {sys_attr_linear(A+1, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_sub(P, R, U),
   sys_simp_sub(S, Q, V)},
   ({U = 1} ->
      sys_attr_inter(V, H, E),
      {sys_inter_lower(E, W, G)};
    {U = -1} ->
      {sys_simp_neg(V, K)},
      sys_attr_inter(K, H, E),
      {sys_inter_upper(E, W, G)};
    {fail}).

% sys_inter_bound(+Pair, +Var, -Goal)
sys_inter_bound(L..H, X, sys_mach_bound(L, H, X)) --> !.
sys_inter_bound(P, X, true) --> [X is P].

% sys_inter_lower(+Pair, +Var, -Goal)
sys_inter_lower(_..H, X, sys_mach_bound(-0rInf, H, X)) :- !.
sys_inter_lower(P, X, sys_mach_bound(-0rInf, P, X)).

% sys_inter_upper(+Pair, +Var, -Goal)
sys_inter_upper(L.._, X, sys_mach_bound(L, 0rInf, X)) :- !.
sys_inter_upper(P, X, sys_mach_bound(P, 0rInf, X)).

/***********************************************************/
/* Linear Forms                                            */
/***********************************************************/

% sys_attr_linear(+Expr, +Var, -Pair)
sys_attr_linear((V :: _), W, (1,0)) :- W == V, !.
sys_attr_linear(T, _, (0,T)) :- T = (_ :: _), !.
sys_attr_linear(-A, W, (R,S)) :- !,
   sys_attr_linear(A, W, (P,Q)),
   sys_simp_neg(P, R),
   sys_simp_neg(Q, S).
sys_attr_linear(A+B, W, (U,V)) :- !,
   sys_attr_linear(A, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_add(P, R, U),
   sys_simp_add(Q, S, V).
sys_attr_linear(A-B, W, (U,V)) :- !,
   sys_attr_linear(A, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   sys_simp_sub(P, R, U),
   sys_simp_sub(Q, S, V).
sys_attr_linear(A*B, W, (U,V)) :- !,
   sys_attr_linear(A, W, (P,Q)),
   sys_attr_linear(B, W, (R,S)),
   (P = 0 ->
      sys_simp_mul(Q, R, U),
      sys_simp_mul(Q, S, V);
    R = 0 ->
      sys_simp_mul(P, S, U),
      sys_simp_mul(Q, S, V);
   fail).
sys_attr_linear(T, _, (0,T)).

% sys_simp_neg(+Expr, -Expr)
sys_simp_neg(A, B) :- integer(A), !, B is -A.
sys_simp_neg(-X, X) :- !.
sys_simp_neg(X, -X).

% sys_simp_add(+Expr, +Expr, -Expr)
sys_simp_add(A, B, C) :- integer(A), integer(B), !, C is A+B.
sys_simp_add(0, Y, Y) :- !.
sys_simp_add(X, 0, X) :- !.
sys_simp_add(X, Y, X+Y).

% sys_simp_sub(+Expr, +Expr, -Expr)
sys_simp_sub(A, B, C) :- integer(A), integer(B), !, C is A-B.
sys_simp_sub(0, Y, R) :- !, sys_simp_neg(Y, R).
sys_simp_sub(X, 0, X) :- !.
sys_simp_sub(X, Y, X-Y).

% sys_simp_mul(+Expr, +Expr, -Expr)
sys_simp_mul(A, B, C) :- integer(A), integer(B), !, C is A*B.
sys_simp_mul(0, _, 0) :- !.
sys_simp_mul(_, 0, 0) :- !.
sys_simp_mul(1, Y, Y) :- !.
sys_simp_mul(X, 1, X) :- !.
sys_simp_mul(-1, Y, R) :- !, sys_simp_neg(Y, R).
sys_simp_mul(X, -1, R) :- !, sys_simp_neg(X, R).
sys_simp_mul(X, Y, X*Y).

/***********************************************************/
/* Cacheable Functors                                      */
/***********************************************************/

/**
 * sys_site_cond(G, F):
 * The predicate succeeds in F with the compilation of G.
 */
% sys_site_cond(+Goal, -Goal)
sys_site_cond(X is B, R) :- !,
   sys_site_point(B, D),
   sys_site_is(X, D, R).
sys_site_cond(sys_mach_bound(A, B, X), R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   sys_site_bound(C, D, X, R).
sys_site_cond(A =:= B, R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   ir_site_new(C =:= D, R).
sys_site_cond(A =\= B, R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   ir_site_new(C =\= D, R).
sys_site_cond(A < B, R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   ir_site_new(C < D, R).
sys_site_cond(A =< B, R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   ir_site_new(C =< D, R).
sys_site_cond(A > B, R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   ir_site_new(C > D, R).
sys_site_cond(A >= B, R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   ir_site_new(C >= D, R).
sys_site_cond(true, R) :- !,
   R = true.
sys_site_cond(G, R) :-
   ir_site_new(G, R).

% sys_site_is(+Expr, +Expr, +Var, -Goal)
sys_site_is(X, A, true) :- integer(A), !,
   X = A.
sys_site_is(X, A, R) :-
   ir_site_new(X is A, R).

% sys_site_bound(+Expr, +Expr, +Var, -Goal)
sys_site_bound(A, B, X, R) :- var(X), integer(A), integer(B), !,
   ir_site_new(between(A, B, X), R).
sys_site_bound(A, B, X, true) :- integer(A), integer(B), !,
   A =< X, X =< B.
sys_site_bound(A, B, X, R) :-
   ir_site_new(sys_mach_bound(A, B, X), R).

/***********************************************************/
/* Expression Folding                                      */
/***********************************************************/

/**
 * sys_site_point(A, P):
 * The predicate succeeds in P with a point expression of A.
 */
% sys_site_point(+Expr, -Expr)
sys_site_point((V :: _), V) :- !.
sys_site_point(-A, R) :- !,
   sys_site_point(A, B),
   sys_site_neg(B, R).
sys_site_point(min(A,B), R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   sys_site_min(C, D, R).
sys_site_point(max(A,B), R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   sys_site_max(C, D, R).
sys_site_point(A+B, R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   sys_site_add(C, D, R).
sys_site_point(A-B, R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   sys_site_sub(C, D, R).
sys_site_point(A*B, R) :- !,
   sys_site_point(A, C),
   sys_site_point(B, D),
   sys_site_mul(C, D, R).
 sys_site_point(T, T).

% sys_site_neg(+Expr, -Expr)
sys_site_neg(A, B) :- integer(A), !,
   B is -A.
sys_site_neg(X, R) :-
   ir_site_new(-X, R).

% sys_site_min(+Expr, +Expr, -Expr)
sys_site_min(A, B, C) :- integer(A), integer(B), !,
   C is min(A,B).
sys_site_min(X, Y, Y) :- X == 0rInf, !.
sys_site_min(X, Y, X) :- Y == 0rInf, !.
sys_site_min(X, Y, R) :-
   ir_site_new(min(X,Y), R).

% sys_site_max(+Expr, +Expr, -Expr)
sys_site_max(A, B, C) :- integer(A), integer(B), !,
   C is max(A,B).
sys_site_max(X, Y, Y) :- X == -0rInf, !.
sys_site_max(X, Y, X) :- Y == -0rInf, !.
sys_site_max(X, Y, R) :-
   ir_site_new(max(X,Y), R).

% sys_site_add(+Expr, +Expr, -Expr)
sys_site_add(A, B, C) :- integer(A), integer(B), !,
   C is A+B.
sys_site_add(X, Y, Y) :- X == 0, !.
sys_site_add(X, Y, X) :- Y == 0, !.
sys_site_add(X, Y, R) :-
   ir_site_new(X+Y, R).

% sys_site_sub(+Expr, +Expr, -Expr)
sys_site_sub(A, B, C) :- integer(A), integer(B), !,
   C is A-B.
sys_site_sub(X, Y, R) :- X == 0, !, sys_site_neg(Y, R).
sys_site_sub(X, Y, X) :- Y == 0, !.
sys_site_sub(X, Y, R) :-
   ir_site_new(X-Y, R).

% sys_site_mul(+Expr, +Expr, -Expr)
sys_site_mul(A, B, C) :- integer(A), integer(B), !,
   C is A*B.
sys_site_mul(X, _, 0) :- X == 0, !.
sys_site_mul(_, Y, 0) :- Y == 0, !.
sys_site_mul(X, Y, R) :-
   ir_site_new(X*Y, R).

/****************************************************************/
/* Error Texts                                                  */
/****************************************************************/

% strings(+Atom, +Atom, -Atom)
:- multifile(strings/3).
strings('domain_error.score_opt', de, 'Scoring erwartet, gefunden ~q..').

strings('resource_error.range_missing', de, 'Domain fehlt.').

strings('domain_error.score_opt', '', 'Scoring expected, found ~q.').

strings('resource_error.range_missing', '', 'Domain missing').
