/*************************************************************************
*									 *
*	 YAP Prolog 							 *
*									 *
*	Yap Prolog was developed at NCCUP - Universidade do Porto	 *
*									 *
  * Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997	 *
*									 *
**************************************************************************
*									 *
* File:		arithpreds.yap						 *
* Last rev:								 *
* mods:									 *
* comments:	arithmetical predicates				 *
*									 *
*************************************************************************/

%% @{

/**
  @file arithpreds.yap

  @addtogroup arithmetic_preds

  
*/

:- system_module(arithmetic_predicates, [
        plus/3,
        succ/2], []).

:- use_system_module( '$_errors', ['$do_error'/2]).


/** @pred succ(? _Int1_:int, ? _Int2_:int) is det
  *

  True if  _Int2_ =  _Int1_ + 1 and  _Int1_ \>= 0. At least
  one of the arguments must be instantiated to a natural number. This
  predicate raises the domain-error not_less_than_zero if called with
  a negative integer. E.g. `succ(X, 0)` fails silently and `succ(X, -1)`
  raises a domain-error. The behaviour to deal with natural numbers
  only was defined by Richard O'Keefe to support the common
  count-down-to-zero in a natural way. 

  */

% M and N nonnegative integers, N is the successor of M
succ(M,N) :-
	(
	 var(M)
	->
	 (
	  integer(N),
	  N > 0
	 ->
	  '$plus'(N,-1,M)
	 ;
	  '$succ_error'(M,N)
	 )
	;
	 integer(M),
	 M >= 0
	->
	 (
	  var(N)
	 ->
	 '$plus'(M,1,N)
	 ;
	  integer(N),
	  N > 0
	 ->
	 '$plus'(M,1,N)
	 ;
	  '$succ_error'(M,N)
	 )
	;
	 '$succ_error'(M,N)
	).

'$succ_error'(M,N) :-
	var(M),
	var(N), !,
	'$do_error'(instantiation_error,succ(M,N)).
'$succ_error'(M,N) :-
	nonvar(M),
	\+ integer(M),
	'$do_error'(type_error(integer, M),succ(M,N)).
'$succ_error'(M,N) :-
	nonvar(M),
	M < 0,
	'$do_error'(domain_error(not_less_than_zero, M),succ(M,N)).
'$succ_error'(M,N) :-
	nonvar(N),
	\+ integer(N),
	'$do_error'(type_error(integer, N),succ(M,N)).
'$succ_error'(M,N) :-
	nonvar(N),
	N < 0,
	'$do_error'(domain_error(not_less_than_zero, N),succ(M,N)).

/** @pred plus(? _Int1_:int, ? _Int2_:int, ? _Int3_:int) is det

  True if  _Int3_ =  _Int1_ +  _Int2_. At least two of the
  three arguments must be instantiated to integers.

  @}

  */

plus(X, Y, Z) :-
       (
        var(X)
       ->
        (
         integer(Y), integer(Z)
        ->
         '$minus'(Z,Y,X)
        ;
         '$plus_error'(X,Y,Z)
        )
       ;
        integer(X)
       ->
        (
         var(Y)
        ->
         (
          integer(Z)
         ->
          '$minus'(Z,X,Y)
         ;
         '$plus_error'(X,Y,Z)
         )
        ;
         integer(Y)
        ->
         (
          integer(Z)
         ->
          '$minus'(Z,Y,X)
         ;
          var(Z)
         ->
          '$plus'(X,Y,Z)
         ;
          '$plus_error'(X,Y,Z)
         )
        ;
         '$plus_error'(X,Y,Z)
        )
       ;
        '$plus_error'(X,Y,Z)
       ).

'$plus_error'(X,Y,Z) :-
       nonvar(X),
       \+ integer(X),
       '$do_error'(type_error(integer, X),plus(X,Y,Z)).
'$plus_error'(X,Y,Z) :-
       nonvar(Y),
       \+ integer(Y),
       '$do_error'(type_error(integer, Y),plus(X,Y,Z)).
'$plus_error'(X,Y,Z) :-
       nonvar(Z),
       \+ integer(Z),
       '$do_error'(type_error(integer, Z),plus(X,Y,Z)).
'$plus_error'(X,Y,Z) :-
       '$do_error'(instantiation_error,plus(X,Y,Z)).