:- pragma(builtins, [once(true)]).
:- use_module(library(error)).
:- use_module(library(lists)).

expand_term((H --> B), Out) :-
	dcg_translate((H --> B), Out), !.

dcg_translate(TermIn, Term) :-
	nonvar(TermIn),
	dcg_rule(TermIn, Term).

:- help(writeln(+term), [iso(false),deprecated(true)]).
:- help(writeln(+stream,+term), [iso(false),deprecated(true)]).

writeln(T) :- write(T), nl.				% SWI
writeln(S, T) :- write(S, T), nl.		% SWI

:- help(predicate_property(+callable,+term), [iso(true)]).

read_from_chars(Cs, T) :-
    read_term_from_chars(Cs, T, []).

:- help(read_from_chars(+chars,?term), [iso(false)]).

predicate_property(P, A) :-
	nonvar(P), atom(A), !,
	must_be(P, callable, predicate_property/2, _),
	'$legacy_predicate_property'(P, A).
predicate_property(P, A) :-
	'$load_properties',
	(	var(A) ->
		true
	; 	(Controls = [
			built_in,choice_construct,
			discontiguous,private,static,
			dynamic,foreign,multifile,
			meta_predicate(_),imported_from(_),template(_),
			iso,visible,tabled
			],
			( memberchk(A, Controls) ->
				true
			;	throw(error(domain_error(predicate_property, A), P))
			)
		)
	),
	must_be(P, callable, predicate_property/2, _),
	(	P = (M:P2) ->
		M:'$predicate_property'(predicate, P2, A)
	;	'$predicate_property'(predicate, P, A)
	).

:- help(evaluable_property(+callable,+term), [iso(true)]).

evaluable_property(P, A) :-
	nonvar(P), atom(A), !,
	must_be(P, callable, evaluable_property/2, _),
	'$legacy_evaluable_property'(P, A).
evaluable_property(P, A) :-
	'$load_properties',
	(	var(A) ->
		true
	; 	(Controls = [iso,built_in,static,dynamic,template(_),template(_,_)],
			(memberchk(A, Controls) ->
				true
			;	(
				must_be(A, callable, evaluable_property/2, _),
				throw(error(domain_error(evaluable_property, A), P))
				)
			)
		)
	),
	must_be(P, callable, evaluable_property/2, _),
	(	P = (M:P2) ->
		M:'$predicate_property'(function, P2, A)
	;	'$predicate_property'(function, P, A)
	).

:- help(current_prolog_flag(+callable,+term), [iso(true)]).

current_prolog_flag(P, A) :-
	nonvar(P), !,
	'$legacy_current_prolog_flag'(P, A).
current_prolog_flag(P, A) :-
	'$load_flags',
	'$current_prolog_flag'(P, A).

:- help(argv(-list), [iso(false)]).
:- help(raw_argv(-list), [iso(false)]).

argv(L) :- current_prolog_flag(argv, L).
raw_argv(L) :- current_prolog_flag(raw_argv, L).

'$post_unify_hook' :-
	'$undo_trail'(Vars, State),
	process_vars_(Vars, [], Goals),
	'$redo_trail'(State),
	xmaplist_(Goals).

xmaplist_([]).
xmaplist_([E|T]) :-
	once(E),
	xmaplist_(T).


process_vars_([], Goals, Goals).
process_vars_([Var-Val|Vars], SoFar, Goals) :-
	(	get_atts(Var, Atts) ->
		process_var_(Atts, Var, Val, SoFar, MoreGoals),
		process_vars_(Vars, MoreGoals, Goals)
	;	process_vars_(Vars, SoFar, Goals)
	).

process_var_([], _, _, Goals, Goals).
process_var_([Att|Atts], Var, Val, SoFar, Goals) :-
	functor(Att, F, A),
	attribute(M, F, A),
	M:verify_attributes(Var, Val, NewGoals),
	append(SoFar, NewGoals, MoreGoals),
	process_var_(Atts, Var, Val, MoreGoals, Goals).

term_attvars_([], VsIn, VsIn).
term_attvars_([H|T], VsIn, VsOut) :-
	(	'$attributed_var'(H) ->
		term_attvars_(T, [H|VsIn], VsOut)
	;	term_attvars_(T, VsIn, VsOut)
	).

term_attributed_variables_(Term, Vs) :-
	can_be(Vs, list, term_attributed_variables_/2, _),
	term_variables(Term, Vs0),
	term_attvars_(Vs0, [], Vs).

:- help(call_residue_vars(@goal,-list), [iso(false)]).
:- meta_predicate(call_residue_vars(0,?)).

call_residue_vars(G, Ls) :-
	'$mark_start'(Mark),
	G,
	'$list_attributed'(Mark, Ls0),
	sort(Ls0, Ls).

duplicate_term_(Term, Copy) :-
	'$duplicate_term'(Term, Copy).

copy_term(Term, Copy, Gs) :-
	duplicate_term_(Term, Copy),
	term_attributed_variables_(Copy, Vs),
	collect_goals_(Vs, [], Gs).

collect_goals_(_, [], GsIn, GsIn).
collect_goals_(V, [H|T], GsIn, GsOut) :-
	nonvar(H),
	H =.. [M, _],
	catch(M:attribute_goals(V, Goal0, []), _, Goal0 = put_atts(V, +H)),
	!,
	(Goal0 = [H2] -> Goal = H2 ; Goal = Goal0),
	collect_goals_(V, T, [Goal|GsIn], GsOut).
/*
collect_goals_(V, [H|T], GsIn, GsOut) :-
	nonvar(H),
	H =.. [F, _],
	attribute(M, F, 1),
	Goal = M:put_atts(V, H),
	collect_goals_(V, T, [Goal|GsIn], GsOut).
*/
collect_goals_(V, [_|T], GsIn, GsOut) :-
	collect_goals_(V, T, GsIn, GsOut).

collect_goals_([], GsIn, GsIn).
collect_goals_([V|T], GsIn, GsOut) :-
	get_atts(V, Ls),
	collect_goals_(V, Ls, GsIn, GsOut2),
	collect_goals_(T, GsOut2, GsOut).

% Debugging...

print_goals_(_, []).
print_goals_(Any, [Goal|Goals]) :-
	(Any -> write(',\n   ') ; true),
	write(Goal),
	(Goals == [] -> true ;	write(', ')),
	print_goals_(false, Goals).

dump_attvars_([], []).
dump_attvars_(Vars, Gs) :-
	collect_goals_(Vars, [], Gs).

dump_attvars_(Any) :-
	'$list_attributed'(0, Vs0),
	sort(Vs0, Vs),
	dump_attvars_(Vs, Gs0),
	flatten(Gs0, Gs1),
	sort(Gs1, Gs),
	print_goals_(Any, Gs).

sort(A, B) :-
	'$sort'(A, B).

:- help(sort(+term,?term), [iso(true)]).

msort(A, B) :-
	'$msort'(A, B).

:- help(msort(+term,?term), [iso(true)]).

keysort(A, B) :-
	'$keysort'(A, B).

:- help(keysort(+term,?term), [iso(true)]).

sort(A, B, C, D) :-
	'$sort'(A, B, C, D).

:- help(sort(+term,+atom,+list,?term), [iso(false)]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%

:- meta_predicate(bagof(-,0,?)).
:- help(bagof(+term,:callable,?list), [iso(true)]).

/**
 * bagof(T, X1^…^Xn^G, L): [ISO 8.10.2]
 * The predicate determines all the solutions to the goal G,
 * whereby collecting copies of the template T and the
 * witness. The predicate then repeatedly succeeds for
 * the witness and the list of associated templates.
 */
% bagof(+Term, +Goal, -List)
bagof(T, G, L) :-
	(var(L) -> true; must_be(L, list_or_partial_list, bagof/3, _)),
	acyclic_term(G),
	sys_globals_kernel_(T^G, W, H),
	findall(W-[+T], H, J),
	sys_same_vars_(J, _),
	keysort(J, K),
	sys_enum_runs_(K, W, L).

:- meta_predicate(setof(-,0,?)).
:- help(setof(+term,+callable,?list), [iso(true)]).

/**
 * setof(T, X1^…^Xn^G, L): [ISO 8.10.3]
 * The predicate determines all the solutions to the goal G,
 * whereby collecting copies of the template T and the
 * witness. The predicate then repeatedly succeeds for
 * the witness and the set of associated templates.
 */
% setof(+Term, +Goal, -List)
setof(T, G, L) :-
	(var(L) -> true; must_be(L, list_or_partial_list, setof/3, _)),
	acyclic_term(G),
	sys_globals_kernel_(T^G, W, H),
	findall(W-[+T], H, J),
	sys_same_vars_(J, _),
	sort(J, K),
	sys_enum_runs_(K, W, L).

% sys_same_vars_(+Pairs, +List)
sys_same_vars_([K-_|L], V) :-
	term_variables(K, V, _),
	sys_same_vars_(L, V).
sys_same_vars_([], _).

% sys_enum_runs_(+Pairs, +Term, -List)
sys_enum_runs_([K-[+V]|L], W, Q) :-
	sys_key_run_(L, K, R, H),
	(K = W, Q = [V|R], (H = [], !; true); sys_enum_runs_(H, W, Q)).

% sys_key_run_(+Pairs, +Term, -List, -Pairs)
sys_key_run_([K-[+V]|L], J, [V|R], H) :- K == J, !,
	sys_key_run_(L, J, R, H).
sys_key_run_(L, _, [], L).

/********************************************************************/
/* Helpers                                                          */
/********************************************************************/

% sys_goal_split_(+Goal, -List, -Goal)
sys_globals_kernel_(G, W, H) :-
	sys_goal_split_(G, I, H),
	term_variables(H, A),
	term_variables(I, B),
	sys_var_subtract_(A, B, W).

% sys_goal_split_(+Goal, -List, -Goal)
sys_goal_split_(G, [], G) :- var(G), !.
sys_goal_split_(V^G, [V|L], H) :- !,
	sys_goal_split_(G, L, H).
sys_goal_split_(G, [], G).

% sys_var_subtract_(+List, +List, -List)
sys_var_subtract_([X|L], R, T) :-
	member(Y, R), Y == X, !,
	sys_var_subtract_(L, R, T).
sys_var_subtract_([X|L], R, [X|S]) :-
	sys_var_subtract_(L, R, S).
sys_var_subtract_([], _, []).

%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Edinburgh...

:- help(get0(?integer), [iso(false),deprecated(true)]).
:- help(get0(+stream,?integer), [iso(false),deprecated(true)]).
:- help(get0(+term), [iso(false),deprecated(true)]).
:- help(get0(+stream,+term), [iso(false),deprecated(true)]).
:- help(put(+integer), [iso(false),deprecated(true)]).
:- help(put(+stream,+integer), [iso(false),deprecated(true)]).
:- help(see(+filename), [iso(false),deprecated(true)]).
:- help(tell(+filename), [iso(false),deprecated(true)]).
:- help(append(+filename), [iso(false),deprecated(true)]).

get0(C) :- get_code(C).
get0(S, C) :- get_code(S, C).
display(T) :- write_canonical(T).
display(S, T) :- write_canonical(S, T).
put(C) :- put_code(C).
put(S,C) :- put_code(S, C).
see(F) :- open(F, read, S), set_input(S).
tell(F) :- open(F, write, S), set_output(S).
append(F) :- open(F, append, S), set_output(S).

%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

'$portray_term'(S, T) :-
	compound(T), !,
	functor(T, _, Args),
	T =.. [Functor|_],
	writeq(S, Functor), write(S, '('),
	(between(1, Args, I),
		arg(I, T, Arg),
		'$portray'(S, Arg),
		(I < Args -> write(S, ',') ; true),
		fail  ; true),
	write(S, ')').
'$portray_term'(S, T) :-
	writeq(S, T).

'$portray'(S, T) :-
	current_output(S0),
	set_output(S),
	((catch(once(portray(T)), _, write(T)), !) ; '$portray_term'(S, T)),
	set_output(S0).

'$portray'(T) :-
	current_output(S),
	((catch(once(portray(T)), _, write(T)), !) ; '$portray_term'(S, T)).

:- help(print(+term), [iso(false)]).
:- help(print(+stream,+term), [iso(false)]).

print(T) :- current_output(S), format(S, "~p", [T]).
print(S, T) :- format(S, "~p", [T]).

:- help(open(+atom,+atom,--stream), [iso(true)]).

open(F, M, S) :- open(F, M, S, []).

:- meta_predicate(engine_create(?,0,?)).

engine_create(T, G, S) :- engine_create(T, G, S, []).

:- help(engine_create(+term,+callable,?stream), [iso(false)]).

engine_post(E, T, R) :-
	engine_post(E, T),
	engine_next(E, R).

current_engine(E) :-
	stream_property(E, engine(true)).

:- help(atomic_list_concat(+list,+atomic), [iso(false)]).

atomic_list_concat(L, Atom) :- atomic_list_concat(L, '', Atom).

:- help(chars_base64(+atom,?atom,+list), [iso(false)]).

chars_base64(Plain, Base64, Opts) :- base64(Plain, Base64, Opts).

:- help(chars_urlenc(+atom,?atom,+list), [iso(false)]).

chars_urlenc(Plain, Url, Opts) :- urlenc(Plain, Url, Opts).

:- help(term_to_atom(+term,?atom), [iso(false)]).

term_to_atom(T, S) :- write_term_to_chars(T, [], S).

:- help(absolute_filename(+atom,?atom), [iso(false)]).

absolute_file_name(R, A) :- absolute_file_name(R, A, []).

:- help(client(+atom,-atom,-atom,--stream), [iso(false)]).

client(Url, S) :- client(Url, _, _, S, []).

:- help(client(+atom,-atom,-atom,--stream), [iso(false)]).

client(Url, Host, Path, S) :- client(Url, Host, Path, S, []).

:- help(server(+atom,--stream), [iso(false)]).

server(Host, S) :- server(Host, S, []).

:- help(load_files(+list), [iso(false)]).

load_files(Files) :- load_files(Files,[]).

:- help(load_files(+list), [iso(false)]).

consult(Files) :- load_files(Files,[]).

:- help(reconsult(+list), [iso(false),deprecated(true)]).

reconsult(Files) :- load_files(Files,[]).

:- help(deconsult(+list), [iso(false),deprecated(true)]).

deconsult(Files) :- unload_files(Files).

:- help('?='(+term,+term), [iso(false)]).

?=(X, Y) :- \+ unifiable(X, Y, [_|_]).

:- help(atom_number(+atom,-number), [iso(false)]).

atom_number(A, N) :- atom_codes(A,Codes), number_codes(N, Codes).

:- help(rational_numerator_denominator(+rational,-integer,-integer), [iso(false)]).

rational_numerator_denominator(R, N, D) :-
	N is numerator(R),
	D is denominator(R).

'$skip_list'(Skip, Xs0, Xs) :- '$skip_max_list'(Skip,_, Xs0, Xs).

:- help(term_hash(+term,+list,-integer), [iso(false)]).

term_hash(Term, _Opts, Hash) :- term_hash(Term, Hash).

read_term_from_chars_(T, Cs, Rest) :-
	'$read_term_from_chars'(T, [], Cs, Rest).

read_term_from_chars_(T, Opts, Cs, Rest) :-
	'$read_term_from_chars'(T, Opts, Cs, Rest).

:- help(read_from_atom(+atom,?term), [iso(false)]).

read_from_atom(A, T) :- read_term_from_atom(A, T, []).

with_output_to(chars(Cs), Goal) :-
	setup_call_cleanup(
		'$capture_output',
		once(Goal),
		'$capture_output_to_chars'(Cs)
	), !.

with_output_to(string(Cs), Goal) :-
	setup_call_cleanup(
		'$capture_output',
		once(Goal),
		'$capture_output_to_chars'(Cs)
	), !.

with_output_to(atom(Cs), Goal) :-
	setup_call_cleanup(
		'$capture_output',
		once(Goal),
		'$capture_output_to_atom'(Cs)
	), !.

iso_dif(X, Y) :-
	X \== Y,
	(	X \= Y ->
		true
	;	throw(error(instantiation_error,iso_dif/2))
	).

:- help(numbervars(+term,+integer,?integer), [iso(false)]).

numbervars(Term, N0, N) :-
   must_be(N0, integer, numbervars/3, _),
   can_be(N, integer, numbervars/3, _),
   term_variables(Term, Vars),
   numberlist_(Vars, N0, N).

numberlist_([], N, N).
numberlist_(['$VAR'(N0)|Vars], N0, N) :-
   N1 is N0+1,
   numberlist_(Vars, N1, N).

:- help(read_line_to_codes(+stream,?list), [iso(false)]).

read_line_to_codes(Stream, Codes) :-
	read_line_to_string(Stream, String),
	string_codes(String, Codes).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%

thread_property(P) :-
	thread_self(Id),
	thread_property(Id, P).

:- meta_predicate(with_mutex(-,0)).

with_mutex(Id, Goal) :-
	setup_call_cleanup(mutex_lock(Id), once(Goal), mutex_unlock(Id)).

:- meta_predicate(thread_create(0,-)).

thread_create(Goal, Id) :-
	thread_create(Goal, Id, []).

thread_send_message(Term) :-
	thread_self(Id),
	thread_send_message(Id, Term).

thread_get_message(Term) :-
	thread_self(Id),
	thread_get_message(Id, Term).

thread_peek_message(Term) :-
	thread_self(Id),
	thread_peek_message(Id, Term).

xthread_statistics(K, V) :-
	thread_self(Id),
	thread_statistics(Id, K, V).

message_queue_create(Id) :-
	message_queue_create(Id, []).

mutex_create(Id) :-
	mutex_create(Id, []).

%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% NOTE: this doesn't display var names properly...

:- help(pretty(+predicateindicator), [iso(false)]).

pretty(PI) :-
	nonvar(PI),
	(   PI = Name/Arity0 ->
		Arity = Arity0
	;   PI = Name//Arity0 ->
		Arity is Arity0 + 2
	;   type_error(predicate_indicator, PI, listing/1)
	),
	functor(Head, Name, Arity),
	\+ \+ clause(Head, _), % only true if there is at least one clause
	(   clause(Head, Body),
		(   Body == true ->
			portray_clause(Head)
		;   portray_clause((Head :- Body))
		),
		false
	;   true
	).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%

:- help(current_op(?integer,?atom,?atom), [iso(true)]).

current_op(A, B, C) :- var(A), var(B), var(C),
	!,
	( '$ops_dirty' -> (retractall('$op'(_,_,_)),'$load_ops') ; true ),
	'$op'(C, B, A).
current_op(_, _, C) :- nonvar(C), \+ atom(C),
	!, throw(error(type_error(atom,C), current_op/3)).
current_op(_, B, _) :- nonvar(B), \+ atom(B),
	!, throw(error(domain_error(operator_specifier, B), current_op/3)).
current_op(_, B, _) :- nonvar(B),
	\+ memberchk(B,[xf, yf, fx, fy, xfx, xfy, yfx]),
	!, throw(error(domain_error(operator_specifier, B), current_op/3)).
current_op(A, _, _) :- nonvar(A),
	\+ integer(A),
	!, throw(error(domain_error(operator_priority, A), current_op/3)).
current_op(A, _, _) :- nonvar(A),
	\+ (A >= 0),
	!, throw(error(domain_error(operator_priority, A), current_op/3)).
current_op(A, _, _) :- nonvar(A),
	\+ (A =< 1200),
	!, throw(error(domain_error(operator_priority, A), current_op/3)).
current_op(A, B, C) :-
	( '$ops_dirty' -> (retractall('$op'(_,_,_)),'$load_ops') ; true ),
	'$op'(C, B, A).

%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%

:- help(sre_match_all_in_file(+pattern,+filename,-list), [iso(false)]).

sre_match_all_in_file(Pat, Filename, L) :-
	setup_call_cleanup(
		open(Filename, read, S, [mmap(Cs)]),
		sre_match_all(Pat, Cs, L),
		close(S)
	).

:- help(sre_match_all(+pattern,+text,-list), [iso(false)]).

sre_match_all(Pat, Text, L) :-
	sre_compile(Pat, Reg),
	sre_match_all_(Reg, Text, [], L2),
	reverse(L2, L).

sre_match_all_(_, [], L, L) :- !.
sre_match_all_(Reg, TextIn, L0, L) :-
	sre_matchp(Reg, TextIn, Match, TextOut),
	(	TextOut \= [] ->
		sre_match_all_(Reg, TextOut, [Match|L0], L)
	;	L = L0
	).

:- help(sre_match_all_pos_in_file(+pattern,+filename,-list), [iso(false)]).

sre_match_all_pos_in_file(Pat, Filename, L) :-
	setup_call_cleanup(
		open(Filename, read, S, [mmap(Cs)]),
		sre_match_all_pos(Pat, Cs, L),
		close(S)
	).

:- help(sre_match_all_pos(+pattern,+subst,-list), [iso(false)]).

sre_match_all_pos(Pat, Text, L) :-
	sre_compile(Pat, Reg),
	sre_match_all_pos_(Reg, Text, 0, [], L2),
	reverse(L2, L).

sre_match_all_pos_(_, [], _, L, L) :- !.
sre_match_all_pos_(Reg, TextIn, Offset, L0, L) :-
	sre_matchp(Reg, TextIn, Match, TextOut),
	string_length(TextIn, N0),
	string_length(Match, N1),
	string_length(TextOut, N2),
	Pos is N0 - (N1 + N2) + Offset,
	Pos2 is Pos + 1,
	( TextOut \= [] ->
		sre_match_all_pos_(Reg, TextOut, Pos2, [Pos-N1|L0], L)
	;	L = L0
	).

:- help(sre_subst_all_in_file(+pattern,+filename,+subst,-list), [iso(false)]).

sre_subst_all_in_file(Pat, Filename, Subst, L) :-
	setup_call_cleanup(
		open(Filename, read, S, [mmap(Cs)]),
		sre_subst_all(Pat, Cs, Subst, L),
		close(S)
	).

:- help(sre_subst_all(+pattern,+text,+subst,-text), [iso(false)]).

sre_subst_all(Pat, Text, Subst, L) :-
	sre_compile(Pat, Reg),
	sre_subst_all_(Reg, Text, Subst, [], L0),
	reverse(L0, L1),
	append(L1, L).

sre_subst_all_(_, [], _, L, L) :- !.
sre_subst_all_(Reg, TextIn, Subst, L0, L) :-
	sre_substp(Reg, TextIn, Prefix, TextOut),
	(	TextOut \= [] ->
		sre_subst_all_(Reg, TextOut, Subst, [Subst,Prefix|L0], L)
	;	L = [Prefix|L0]
	).

%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

/********************************************************/
/* Float Approximation                                  */
/********************************************************/

:- help(rationalize(+number,-rational), [iso(false)]).

/**
 * See also:
 * Continued Fractions on the Stern-Brocot Tree
 * https://www.cut-the-knot.org/blue/ContinuedFractions.shtml
 */

/**
 * rationalize(X):
 * If X is a number then the function returns an approximate rational number.
 */
% rationalize(+Number, -Rational)
rationalize(F, C/B) :- F < 0, !,
   H is -F,
   rationalize(H, A/B),
   C is -A.
rationalize(F, R) :-
   rat_start(F, V, W),
   divmod(V, W, D, U),
   rat_iter(W/U, D/1, 1/0, F, R).

% rat_start(+Number, -Integer, -Integer)
rat_start(F, V, W) :-
   parts(F, M, E),
   (E < 0 ->
       V = M, W is 2^(-E);
       V is M*E^2, W = 1).

% rat_iter(+Rational, +Rational, +Rational, +Number, -Rational)
rat_iter(_, X, _, Y, X) :- X =:= Y, !.
rat_iter(_/0, X, _, _, X) :- !.
rat_iter(V/W, M/N, P/Q, Y, X) :-
   divmod(V, W, D, U),
   R is D*M+P,
   S is D*N+Q,
   rat_iter(W/U, R/S, M/N, Y, X).

/********************************************************/
/* IEEE Simulation                                      */
/********************************************************/

% parts(+Number, -Integer, -Integer)
parts(F, M, E) :-
   logb(F, G),
   E is G-52,
   U is -E,
   scalb(F, U, N),
   M is truncate(N).

% scalb(+Number, +Integer, -Number)
scalb(M, E, R) :-
   R is M*2**E.

% logb(+NUmber, -Integer)
logb(M, E) :-
   E is floor(log(M)/log(2)).

goal_expansion(maplist(G, L1), Goal) :-
	nonvar(G), !,
	term_variables(G, Args),
	gensym:gensym(maplist_, U),
	Goal =.. [U,Args,L1],
	G1 =.. [U,Args,[]],
	user:'$assertz'(G1),							% not dynamic
	G2a =.. [U,Args,[E1|T1]],
	G2b =.. [U,Args,T1],
	user:'$assertz'((G2a :- call(G, E1), G2b)),		% not dynamic
	true.
goal_expansion(maplist(G, L1), maplist(G, L1)).

goal_expansion(maplist(G, L1, L2), Goal) :-
	nonvar(G), !,
	term_variables(G, Args),
	gensym:gensym(maplist_, U),
	Goal =.. [U,Args,L1,L2],
	G1 =.. [U,Args,[],[]],
	user:'$assertz'(G1),							% not dynamic
	G2a =.. [U,Args,[E1|T1],[E2|T2]],
	G2b =.. [U,Args,T1,T2],
	user:'$assertz'((G2a :- call(G, E1, E2), G2b)),	% not dynamic
	true.
goal_expansion(maplist(G, L1, L2), maplist(G, L1, L2)).

goal_expansion(call_det(G, Det), Goal) :-
	nonvar(G),
	!,
	Goal = ('$get_level'(L1), call(G), '$get_level'(L2), (L1 = L2 -> Det = true; Det = false)),
	true.
goal_expansion(call_det(G, V), call_det(G, V)).
