/*************************************************************************
*									 *
*	 YAP Prolog 							 *
*									 *
*	Yap Prolog was developed at NCCUP - Universidade do Porto	 *
*									 *
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997	 *
*									 *
**************************************************************************
*									 *
* File:		callcount.yap						 *
* Last rev:	8/2/02							 *
* mods:									 *
* comments:	Some profiling predicates available in yap		 *
*									 *
*************************************************************************/


/**
 *    @file callcount.yap
 *   @short support call counting.
 *   
 *   @defgroup Profiling Profiling Prolog Programs
 *   @brief the clock and the tick profilers.
 *   @ingroup extensions
 *   @{
 *
 * YAP includes two profilers. The count profiler keeps information on the
 * number of times a predicate was called. This information can be used to
 * detect what are the most commonly called predicates in the program.  The
 * count profiler can be compiled by setting YAP's flag profiling
 * to `on`. The time-profiler is a `gprof` profiler, and counts
 * how many ticks are being spent on specific predicates, or on other
 * system functions such as internal data-base accesses or garbage collects.
 * 
 *     + Call_Counting
 *     +
 * 
 */

/**
@}
*/


/**
 *  @defgroup Call_Counting Counting Calls
 *  @ingroup Profiling
 *  @{ 
 *  
 * Predicates compiled with YAP's flag call_counting set to
 * `on` update counters on the numbers of calls and of
 * retries. Counters are actually decreasing counters, so that they can be
 * used as timers.  Three counters are available:
 * 
 * + `calls`: number of predicate calls since execution started or since
 * system was reset; 
 * + `retries`: number of retries for predicates called since
 * execution started or since counters were reset;
 * + `calls_and_retries`: count both on predicate calls and
 * retries.
 * 
 * These counters can be used to find out how many calls a certain
 * goal takes to execute. They can also be used as timers.
 * 
 * The code for the call counters piggybacks on the profiling
 * code. Therefore, activating the call counters also activates the profiling
 * counters.
 * 
 * These are  the predicates that access and manipulate the call counters. 
 * */

:- system_module( '$_callcount', [call_count/3,
        call_count_data/3,
        call_count_reset/0], []).

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


/** @pred  call_count_data(- _Calls_, - _Retries_, - _CallsAndRetries_) 
 * 
 * 
 * Give current call count data. The first argument gives the current value
 * for the  _Calls_ counter, next the  _Retries_ counter, and last
 * the  _CallsAndRetries_ counter.
 *  
 * */
call_count_data(Calls, Retries, Both) :-
	'$call_count_info'(Calls, Retries, Both).

/** @pred  call_count_reset 
 * 
 * 
 * Reset call count counters. All timers are also reset.
 * 
 */
call_count_reset :-
	'$call_count_reset'.

/** @pred  call_count(? _CallsMax_, ? _RetriesMax_, ? _CallsAndRetriesMax_) 
 * 
 * 
 * Set call counters as timers. YAP will generate an exception
 * if one of the instantiated call counters decreases to 0:
 * 
 * + _CallsMax_
 * 
 *     throw the exception `call_counter` when the
 * counter `calls` reaches 0;
 * 
 * + _RetriesMax_
 * 
 *     throw the exception `retry_counter` when the
 * counter `retries` reaches 0;
 * 
 * + _CallsAndRetriesMax_
 * 
 *     throw the exception
 * `call_and_retry_counter` when the counter `calls_and_retries`
 * reaches 0.
 * 
 *  YAP will ignore counters that are called with unbound arguments.
 * 
 * Next, we show a simple example of how to use call counters:
 * 
 * ~~~~~{.prolog}
 *    ?- yap_flag(call_counting,on), [-user]. l :- l. end_of_file. yap_flag(call_counting,off).
 * 
 * yes
 * 
 * yes
 *    ?- catch((call_count(10000,_,_),l),call_counter,format("limit_exceeded.~n",[])).
 * 
 * limit_exceeded.
 * 
 * yes
 * ~~~~~
 * Notice that we first compile the looping predicate `l/0` with
 * call_counting `on`. Next, we catch/3 to handle an
 * exception when `l/0` performs more than 10000 reductions.
 * 
 * 
 */
call_count(Calls, Retries, Both) :-
	'$check_if_call_count_on'(Calls, CallsOn),
	'$check_if_call_count_on'(Retries, RetriesOn),
	'$check_if_call_count_on'(Both, BothOn),
	'$call_count_set'(Calls, CallsOn, Retries, RetriesOn, Both, BothOn).

'$check_if_call_count_on'(Calls, 1) :- integer(Calls), !.
'$check_if_call_count_on'(Calls, 0) :- var(Calls), !.
'$check_if_call_count_on'(Calls, A) :-
	'$do_error'(type_error(integer,Calls),call_count(A)).

%% @}